---
openapi: 3.0.3

info:
  title: "Subscription Custom Fields API"
  version: 1.0.4
  description: |

    #### Copyright
    © Aeris Communications, Inc. 2026. All rights reserved. No part of this document may be reproduced in any form without the written permission of the copyright owner.

    #### Disclaimer

    The contents of this document are subject to revision without notice due to continued progress in methodology, design and manufacturing.
    Aeris shall have no liability for any error or damage of any kind resulting from the use of this document.

    #### Trademark List

    All trademarks mentioned herein are the property of their respective owners.
    These are shown in the document Trademark Information.

    # Introduction

    This document describes the IoT Accelerator Subscription Search API that provides resources
    to retrieve an organization's subscription detail information.
    The Subscription Search API is based on the Representational State Transfer (REST) interface.
    From this point on, the Subscription Search API is referred to as API in this document.

    ## Scope

    This document covers the following topics:

    — An overview of the API

    — A detailed description of all operations offered by the API

    ## Prerequisites
    In this document, it is assumed that the user is familiar with the following:

    — Java Script Object Notation (JSON)

    — Basic understanding of REST web services

    — The [OpenAPI Initiative](https://www.openapis.org/) for the standardized format used in this document.

    ## Related Information
    The definition and explanation of acronyms and terminology and typographic conventions can be found in the following documents:

    — IoTA Glossary of Terms and Acronyms

    — Typographic Conventions

    # General API Guidelines

    ## Compatibility

    API audience must be prepared for compatible API extensions:

    - Be tolerant with unknown fields in the payload (see also Fowler’s "TolerantReader" post), i.e. ignore new fields but do not eliminate them from payload if needed for subsequent PUT requests.
    - Be prepared that "x-extensible-enum" return parameter may deliver new values. Either be agnostic or provide default behavior for unknown values.
    - Be prepared to handle HTTP status codes not explicitly specified in endpoint definitions.
    - Follow the redirect when the server returns HTTP status 301 Moved Permanently.

    ## Security

    This section describes the security measures in the API. Security measures include authentication, authorization, and encryption.

    ### Authentication and Authorization

    The API uses OAuth2 and OpenID Connect (OIDC) for authentication and authorization.

    All API endpoints require authentication, which is performed by passing a token in the Authorization header in each request, preceded by the word Bearer and a space. See the following example:
    ```
    curl -i -X GET "http://<baseURL>/iot/api/shared-bundles" -H "Content-Type: application/json" -H "Authorization: Bearer <TOKEN_GOES_HERE>"
    ```
    To fetch the token using e.g. curl, submit the following:
    ```
    curl -X POST "https://<baseURL>/iot/api/auth/token" -H "Accept: application/vnd.dcp-v1+json" -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=password&client_id=<client_id>&username=<username>&password=<password>"
    ```
    ### Encryption

    All API requests are encrypted using the HTTPS protocol to grant secure data transfer.

    
    ## Deprecation

    API deprecation will be reflected in the OpenAPI definition. If a method on a path, a whole path or even a whole API endpoint (multiple paths) should be deprecated, they will be marked as deprecated.
    During deprecation phase, a Warning header (see RFC 7234 - Warning header) field will be added in the response. The warn-code will be 299 and the warn-text will inform about what is deprecated and when it will be completely removed.

    API audience should not start using a deprecated API.

    API audience is encouraged to monitor for Warning header.

  x-api-id: "c866057a-0e0d-4409-87f6-62289b4d363e"
  x-audience: "external-partner"

servers:
  - url: https://iot-api.aeris.com/iot/api/subscriptions
    description: API server

paths:
  "/custom-fields":
    post:
      tags:
        - Subscription Custom Fields
      summary: "Create a custom field (key)"
      description: >
        Create a new custom field
      operationId: "create-custom-field"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomField'
      responses:
        "201":
          description: "Custom field created"
          allOf:
            - $ref: '#/components/responses/RateLimitedResponse'
        "400":
          description: "Bad Request"
          content:
            "application/problem+json":
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                problem-response-example:
                  value:
                    - type: about:blank
                      title: Bad Request
                      status: 400
                      detail: organization already has maximum number of fields
                      instance: null
        "403":
          $ref: '#/components/responses/Response_403'
        "429":
          $ref: '#/components/responses/Response_429'
        "500":
          $ref: '#/components/responses/Response_500'
        default:
          $ref: '#/components/responses/Response_500'
      security:
        - Oauth2_auth:
            - "custom-field.write"
    get:
      tags:
        - Subscription Custom Fields
      summary: "Get custom fields"
      description: "Get custom fields"
      operationId: "get-custom-fields"
      responses:
        "200":
          description: >
            OK.
          allOf:
            - $ref: '#/components/responses/RateLimitedResponse'
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CustomFieldsResponse"
              examples:
                get-custom-fields-response-example:
                  value:
                    items:
                      - id: 61
                        companyId: 85000018
                        organizationId: 3.85.3.18
                        fieldName: Test name
                        type: FREE_TEXT
        "403":
          $ref: '#/components/responses/Response_403'
        "429":
          $ref: '#/components/responses/Response_429'
        "500":
          $ref: '#/components/responses/Response_500'
        default:
          $ref: '#/components/responses/Response_500'
      security:
        - Oauth2_auth:
            - "custom-field.read"

  "/custom-fields/{id}":
    put:
      tags:
        - Subscription Custom Fields
      summary: "Update custom field predefined values"
      description: >
        Update existing custom field.
      operationId: "update-custom-field"
      parameters:
        - name: id
          in: path
          schema:
            type: string
            example: "123456789"
          required: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CustomField'
      responses:
        "200":
          description: "OK"
          allOf:
            - $ref: '#/components/responses/RateLimitedResponse'
        "400":
          description: "Bad Request"
          content:
            "application/problem+json":
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                bad-request-response-example:
                  value:
                    - type: about:blank
                      title: Bad Request
                      status: 400
                      detail: name is not allowed
                      instance: null
        "403":
          $ref: '#/components/responses/Response_403'
        "404":
          description: "Not Found"
          content:
            "application/problem+json":
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                not-found-response-example:
                  value:
                    - type: about:blank
                      title: Not Found
                      status: 404
                      detail: "field not found: xyz"
                      instance: null
        "429":
          $ref: '#/components/responses/Response_429'
        "500":
          $ref: '#/components/responses/Response_500'
        default:
          $ref: '#/components/responses/Response_500'
      security:
        - Oauth2_auth:
            - "custom-field.write"

    delete:
      tags:
        - Subscription Custom Fields
      summary: "Delete a custom field"
      description: >
        Delete a custom field.
      operationId: "delete-custom-field"
      parameters:
        - name: id
          in: path
          schema:
            type: string
            example: "123456789"
          required: true
      responses:
        "200":
          description: >
            OK.
          allOf:
            - $ref: '#/components/responses/RateLimitedResponse'
        "403":
          $ref: '#/components/responses/Response_403'
        "404":
          description: "Not Found"
          content:
            "application/problem+json":
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                not-found-response-example:
                  value:
                    - type: about:blank
                      title: Not Found
                      status: 404
                      detail: "field not found: xyz"
                      instance: null
        "429":
          $ref: '#/components/responses/Response_429'
        "500":
          $ref: '#/components/responses/Response_500'
        default:
          $ref: '#/components/responses/Response_500'
      security:
        - Oauth2_auth:
            - "custom-field.write"

  "/custom-fields/requests":
    post:
      tags:
        - Subscription Custom Fields
      summary: "Add or update custom field value for subscription"
      description: >
        Create a subscription custom field update request for updating
        multiple subscriptions with one request.
      operationId: "request-custom-field-update"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateCustomFieldRequest'
      responses:
        "201":
          description: >
            Created, Location header will contain the created request url
          allOf:
            - $ref: '#/components/responses/RateLimitedResponse'
        "400":
          description: "Bad Request"
          content:
            "application/problem+json":
              schema:
                $ref: '#/components/schemas/Problem'
              examples:
                bad-request-response-example:
                  value:
                    - type: about:blank
                      title: Bad Request
                      status: 400
                      detail: no identifiers provided
                      instance: null
        "403":
          $ref: '#/components/responses/Response_403'
        "429":
          $ref: '#/components/responses/Response_429'
        "500":
          $ref: '#/components/responses/Response_500'
        default:
          $ref: '#/components/responses/Response_500'
      security:
        - Oauth2_auth:
            - "subscription.write"

components:
  headers:
    Content-Type:
      description: Handle Content-Type
      schema:
        type: string
    X-RateLimit-Limit-Second:
      description: The maximum number of requests allowed in a second.
      schema:
        type: integer
        format: int32
    X-RateLimit-Limit-Minute:
      description: The maximum number of requests allowed in a minute.
      schema:
        type: integer
        format: int32
    X-RateLimit-Remaining-Second:
      description: The number of requests remaining in a second.
      schema:
        type: integer
        format: int32
    X-RateLimit-Remaining-Minute:
      description: The number of requests remaining in a minute.
      schema:
        type: integer
        format: int32
  securitySchemes:
    Oauth2_auth:
      flows:
        password:
          tokenUrl: https://iot-api.aeris.com/iot/api/auth/token
          scopes:
            custom-field.write: create or update custom fields
            custom-field.read: read custom fields
            subscription.write: update subscription
      type: oauth2

  schemas:

    CustomFieldType:
      type: "string"
      description: "Type of custom field"
      x-extensible-enum:
        - PREDEFINED_FIELD_VALUES
        - FREE_TEXT
      example: PREDEFINED_FIELD_VALUES

    IdType:
      type: "string"
      description: "Type of identifier"
      x-extensible-enum:
        - IMSI
      example: IMSI

    UpdateCustomFieldRequest:
      type: "object"
      properties:
        identifierType:
          $ref: "#/components/schemas/IdType"
        organizationId:
          type: "string"
          example: "1.23.45. If not specified, default company id of user is used."
        identifiers:
          description: >
            List of identifiers to update.  Max number of items allowed is 100k.
          type: "array"
          items:
            type: "string"
          example: ["123456789012345", "123456789012346"]
        customFields:
          $ref: '#/components/schemas/SubscriptionCustomFields'
    CustomField:
      type: "object"
      required:
        - fieldName
        - type
      anyOf:
        - required: ["companyId", "fieldName", "type"]
        - required: ["organizationId", "fieldName", "type"]
      properties:
        id:
          type: "string"
          example: "123456789"
        companyId:
          type: "string"
          example: "84000001"
          description: "One of companyId or organizationId is required"
        organizationId:
          type: "string"
          example: "84.1"
          description: "One of companyId or organizationId is required"
        fieldName:
          type: "string"
          example: "color"
          description: >
            Acceptable characters are -
            alphanumeric,
            - (hyphen),
            _ (underscore),
            Empty character in the middle of character sequence
          maxLength: 60
        type:
          $ref: "#/components/schemas/CustomFieldType"
        predefinedValues:
          description: >
            List of predefined values.
          type: "array"
          items:
            type: "string"
          example: ["red"]

    CustomFieldsResponse:
      type: "object"
      properties:
        items:
          description: >
            Array of custom fields
          type: "array"
          items:
            $ref: "#/components/schemas/CustomField"

    SubscriptionCustomFields:
      type: "array"
      items:
        $ref: "#/components/schemas/AttachedCustomField"

    AttachedCustomField:
      type: "object"
      required:
        - fieldName
        - fieldValue
      properties:
        fieldName:
          type: "string"
          example: "color"
          description: >
            Acceptable characters are -
            alphanumeric,
            - (hyphen),
            _ (underscore),
            Empty character in the middle of character sequence
          maxLength: 60
        fieldValue:
          type: "string"
          example: "red"
          description: >
            Acceptable characters are -
            alphanumeric,
            - (hyphen),
            _ (underscore),
            : (colon)
            ? (question mark)
            ( (open parenthesis)
            ) (close parenthesis)
            Empty character in the middle of character sequence
          maxLength: 60
        fieldId:
          type: "integer"
          format: "int64"
          example: "123456"

    Problem:
      type: object
      example: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4
      properties:
        type:
          type: string
          format: uri
          default: 'about:blank'
          description: |
            An absolute URI that identifies the problem type. When
            dereferenced,it SHOULD provide human-readable documentation
            for the problem type (e.g., using HTML).
        title:
          type: string
          description: |
            A short summary of the problem type in english and readable for
            engineers.
          example: 'Bad Request'
        status:
          type: integer
          format: int32
          description: |
            The HTTP status code generated by the origin server for this
            occurrence of the problem.
          minimum: 100
          maximum: 600
          exclusiveMaximum: true
          example: 400
        detail:
          type: string
          description: |
            A human readable explanation specific to this occurrence of the
            problem.
          example: 'Uknown details'
        instance:
          type: string
          description: |
            An absolute URI that identifies the specific occurrence of the
            problem. It may or may not yield further information if
            dereferenced.
  responses:
    RateLimitedResponse:
      headers:
        X-RateLimit-Limit-Second:
          $ref: '#/components/headers/X-RateLimit-Limit-Second'
        X-RateLimit-Limit-Minute:
          $ref: '#/components/headers/X-RateLimit-Limit-Minute'
        X-RateLimit-Remaining-Second:
          $ref: '#/components/headers/X-RateLimit-Remaining-Second'
        X-RateLimit-Remaining-Minute:
          $ref: '#/components/headers/X-RateLimit-Remaining-Minute'
        Content-Type:
          $ref: '#/components/headers/Content-Type'
    Response_403:
      description: "Forbidden"
      content:
        "application/problem+json":
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            problem-response-example:
              value:
                - type: about:blank
                  title: Forbidden
                  status: 403
                  detail: null
                  instance: null
    Response_429:
      description: "Too Many Requests"
      allOf:
        - $ref: '#/components/responses/RateLimitedResponse'
      content:
        "application/problem+json":
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            problem-response-example:
              value:
                - type: about:blank
                  title: Too Many Requests
                  status: 429
                  detail: Too Many Requests
                  instance: null
    Response_500:
      description: "Internal Server Error"
      content:
        "application/problem+json":
          schema:
            $ref: '#/components/schemas/Problem'
          examples:
            problem-response-example:
              value:
                - type: about:blank
                  title: Internal Server Error
                  status: 500
                  detail: null
                  instance: null
tags:
  - name: Subscription Custom Fields
    description: |
      Custom fields are user-defined key-value pairs. They can be used for
      advanced filtering capabilities on single subscriptions as well as batch
      jobs. Custom fields can be created, modified and attached to
      subscriptions using this api.
