openapi: 3.1.0
info:
  contact:
    email: info@dke-data.com
    name: DKE Data
    url: https://dke-data.com
  title: agrirouter G4 API
  version: "1.0"
  description: >
    This service provides access to agrirouter API through modern RESTful
    interface called G4 (for gateway id = 4).
servers:
  - url: https://api.agrirouter.com
    description: Production Server
  - url: https://api.qa.agrirouter.farm
    description: QA Server
paths:
  /messages:
    post:
      operationId: sendMessages
      summary: Send one or several messages to agrirouter inbox
      description: |
        Send one or several messages via agrirouter to other endpoints.
        The payload is sent as a binary stream in the request body.
        If the payload is too large, it would be split into several messages
        by agrirouter itself, client does not need to care about doing that.
        Depending on size of the payload, result could be a single message
        or several messages in the target endpoint outboxes.
      parameters:
        - name: content-length
          in: header
          required: true
          schema:
            type: integer
            format: int64
          description: |
            The size of the complete payload in bytes.
            This is used to determine if the payload needs to be 
            split into chunks and how.
        - name: x-agrirouter-is-publish
          in: header
          required: true
          schema:
            type: boolean
          description: >
            If set to true, the message will be sent as a published message

            and any endpoints that are subscribed to the message type

            would be able to receive it, provided other conditions for routing
            are met.
        - name: x-agrirouter-direct-recipients
          in: header
          required: false
          schema:
            type: array
            items:
              type: string
              format: uuid
          description: >
            Comma-separated list of agrirouter endpoint IDs of the direct
            recipients.

            Allows specifying direct recipients of the message, which

            could receive it even if they are not subscribed to the message
            type.
        - name: x-agrirouter-sent-timestamp
          in: header
          required: true
          schema:
            type: string
            format: date-time
          description: |
            Client side timestamp of sending the data.
        - name: x-agrirouter-endpoint-id
          in: header
          required: true
          schema:
            type: string
            format: uuid
          description: |
            The agrirouter endpoint ID of the sender.
            This is the ID of the endpoint that is sending the message.
        - name: x-agrirouter-teamset-context-id
          in: header
          required: false
          schema:
            type: string
            maxLength: 100
          description: >
            A teamset is a set of connected machines that work and move
            together 

            and are connected to the same (virtual) communication unit. 

            The machines in the teamset are typically connected physically and 

            informationally (for example via ISOBUS).
        - name: x-agrirouter-message-type
          in: header
          required: true
          schema:
            type: string
            maxLength: 100
          description: |
            Message type of the sent data. See available types here:
            https://docs.agrirouter.com/agrirouter-interface-documentation/latest/tmt/overview.html
          examples:
            iso11783DeviceDescriptionProtobuf:
              summary: Device Description
              value: iso:11783:-10:device_description:protobuf
            iso11783TaskDataZip:
              summary: ISO11783 Task Data Zip
              value: iso:11783:-10:taskdata:zip
        - $ref: "#/components/parameters/x-agrirouter-tenant-id"
        - name: x-agrirouter-context-id
          in: header
          required: true
          schema:
            type: string
            format: uuid
          description: |
            Application-generated UUID identifying the sent payload. Required
            on every send. Generate a fresh UUID per payload; reuse the same
            UUID only when retrying the exact same payload after a failure,
            so agrirouter can deduplicate.

            agrirouter exposes this value to the receiving endpoint as the
            `app_message_id` field on MESSAGE_RECEIVED events, and uses it as
            the chunk context id when the payload is split across chunks.
        - name: x-agrirouter-filename
          in: header
          required: false
          schema:
            type: string
            maxLength: 100
          description: |
            Optional name of the file that is attached to messages as metadata.
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        "200":
          description: Message successfully sent to inbox
        "400":
          description: Request is invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Authorization failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "413":
          description: Content too large
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server Side Error
  /events:
    get:
      operationId: receiveEvents
      summary: Receive events from agrirouter outbox
      parameters:
        - name: types
          in: query
          required: false
          description: >
            Events type filter, if provided would limit the events returned to
            only those of the specified types.

            If not provided, all supported events will be streamed.
          example:
            - MESSAGE_RECEIVED
            - FILE_RECEIVED
            - AUTHORIZATION_ADDED
          schema:
            type: array
            items:
              type: string
              enum:
                - MESSAGE_RECEIVED
                - FILE_RECEIVED
                - ENDPOINT_DELETED
                - ENDPOINTS_LIST_CHANGED
                - AUTHORIZATION_ADDED
                - AUTHORIZATION_REVOKED
      description: |
        Receive stream of events from the agrirouter outbox.
        The events are retrieved for every endpoint of an
        authorized application.

        These are possible event types:
        - MESSAGE_RECEIVED - when one message is received
        - FILE_RECEIVED - when chunked payload has been completely received
        - ENDPOINTS_LIST_CHANGED - when the list of endpoints in a tenant changes
        - AUTHORIZATION_ADDED - when a tenant authorization is newly added
        - AUTHORIZATION_REVOKED - when a tenant authorization is revoked

        These events are sent as Server-Sent Events (SSE), where one line
        prefixed "event:" would define event type and another line
        prefixed "data:" would contain the actual event data.

        See Server-Sent Events specification here:
        https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events

        For example:

        ```
        event: MESSAGE_RECEIVED
        data: {"messageId":"12345","timestamp":"2023-01-01T12:00:00Z"}
        ```

        In this API, `data` would always be JSON encoded object, every
        event data schema is defined in the `#/components/schemas`, with
        schema name always ending with `EventData`. 

        Here is a table:

        | Event Type          | Schema                                                                     |
        |---------------------|----------------------------------------------------------------------------|
        | MESSAGE_RECEIVED    | [MessageReceivedEventData](#/components/schemas/MessageReceivedEventData)  |
        | FILE_RECEIVED       | [FileReceivedEventData](#/components/schemas/FileReceivedEventData)        |
        | ENDPOINT_DELETED    | [EndpointDeletedEventData](#/components/schemas/EndpointDeletedEventData)  |
        | ENDPOINTS_LIST_CHANGED | [EndpointsListChangedEventData](#/components/schemas/EndpointsListChangedEventData) |
        | AUTHORIZATION_ADDED | [AuthorizationAddedEventData](#/components/schemas/AuthorizationAddedEventData) |
        | AUTHORIZATION_REVOKED | [AuthorizationRevokedEventData](#/components/schemas/AuthorizationRevokedEventData) |
      responses:
        "200":
          description: Stream of events from agrirouter
          content:
            text/event-stream:
              schema:
                $ref: "#/components/schemas/GenericEventData"
        "400":
          description: Request is invalid
        "401":
          description: Authentication failed
        "403":
          description: Authorization failed
        "500":
          description: Server Side Error
  /confirmations:
    post:
      operationId: confirmMessages
      summary: Confirm received messages
      description: >
        Confirm that messages have been received and processed by the
        application.

        Each confirmation carries a message ID and the endpoint ID that received
        it.

        The same message may be received by different endpoints and can be
        confirmed

        separately, either in the same request or in different ones.
      parameters:
        - $ref: "#/components/parameters/x-agrirouter-tenant-id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfirmMessagesRequest"
      responses:
        "202":
          description: Confirmation is saved for processing, feed will be updated eventually
        "400":
          description: Request is invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Authorization failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server Side Error
  /tenants/{tenantId}/endpoints:
    get:
      operationId: listTenantEndpoints
      summary: List tenant endpoints with route information
      description: >
        Returns the current list of endpoints in the tenant together with their

        capability information.


        For endpoints owned by the authorized application, this operation also

        returns route-derived maps describing which endpoints they can send to

        and receive from for each message type.


        This operation is tenant-scoped and is intended for inspecting or

        refreshing endpoint information for one already-known tenant. Use it

        for example if you cannot/do not want to persist information from the

        `ENDPOINT_LIST_CHANGED` event or if your frontend doesn't have access to
        that

        data.


        It is not intended as the primary application-start or global re-sync
        operation.

        For that purpose, use `GET /tenants`, which returns all currently

        authorized tenants together with their related endpoints.
      parameters:
        - $ref: "#/components/parameters/tenantId"
      responses:
        "200":
          description: Tenant endpoints successfully retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EndpointsListResponse"
        "400":
          description: Request is invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Authorization failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server Side Error
  /tenants:
    get:
      operationId: listAuthorizedTenants
      summary: List all authorized tenants and their related endpoints
      description: |
        Returns all tenants for which the current application has an existing
        authorization together with the related endpoints known for each tenant.

        This operation is intended as the primary global synchronization
        endpoint, for example when an application starts, recovers after a
        crash, or otherwise needs to rebuild its complete tenant state.
      responses:
        "200":
          description: Authorized tenants successfully retrieved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TenantsListResponse"
        "400":
          description: Request is invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Authorization failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server Side Error
  /endpoints/{externalId}:
    put:
      operationId: putEndpoint
      summary: Create or update endpoint
      description: This resource could be used to create new endpoint or reconfigure
        existing one.
      security:
        - agrirouterOauthQA:
            - endpoints:manage
        - agrirouterOauthPROD:
            - endpoints:manage
      parameters:
        - $ref: "#/components/parameters/x-agrirouter-tenant-id"
        - $ref: "#/components/parameters/externalId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PutEndpointRequest"
      responses:
        "200":
          description: Endpoint was successfully updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Endpoint"
        "201":
          description: Endpoint was successfully created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Endpoint"
        "400":
          description: Request is invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Authorization failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "413":
          description: Content too large
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server Side Error
        "502":
          description: Bad Gateway, upstream service has returned an error
        "503":
          description: Service Unavailable at the moment
        "504":
          description: Gateway Timeout, request took too long to process
    delete:
      operationId: deleteEndpoint
      summary: Delete endpoint
      description: Delete endpoint by external ID. This would remove the endpoint from
        agrirouter and it would no longer receive messages.
      security:
        - agrirouterOauthQA:
            - endpoints:manage
        - agrirouterOauthPROD:
            - endpoints:manage
      parameters:
        - $ref: "#/components/parameters/x-agrirouter-tenant-id"
        - $ref: "#/components/parameters/externalId"
      responses:
        "204":
          description: Endpoint successfully deleted
        "400":
          description: Request is invalid
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Authorization failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Endpoint with the specified external ID not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Server Side Error
security:
  - agrirouterOauthQA: []
  - agrirouterOauthPROD: []
components:
  securitySchemes:
    agrirouterOauthQA:
      type: oauth2
      description: Agrirouter OAuth2 authentication for QA environment.
      flows:
        clientCredentials:
          tokenUrl: https://oauth.qa.agrirouter.farm/token
          scopes: {}
    agrirouterOauthPROD:
      type: oauth2
      description: Agrirouter OAuth2 authentication for PROD environment.
      flows:
        clientCredentials:
          tokenUrl: https://api-oauth.agrirouter.com/token
          scopes: {}
  parameters:
    x-agrirouter-tenant-id:
      name: x-agrirouter-tenant-id
      in: header
      required: true
      schema:
        type: string
        format: uuid
      description: The farmer's tenant ID in relation to which communication is done.
    tenantId:
      name: tenantId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: The farmer's tenant ID in relation to which this operation is performed.
    externalId:
      name: externalId
      in: path
      required: true
      schema:
        type: string
        minLength: 3
        maxLength: 255
        pattern: ^([uU][rR][nN]:)?[a-zA-Z0-9][a-zA-Z0-9-]{0,31}:[a-zA-Z0-9()+,\-.:=@;$_!*%/?#]+$
      description: >
        Application-defined external ID of the endpoint. SHOULD be formatted as
        a

        URN as defined by RFC 8141 (e.g. `urn:my-app:endpoint:42`). The `urn:`

        prefix itself is optional for backwards compatibility, but the value
        must

        always contain a namespace identifier and a namespace-specific string

        separated by a colon. The URN namespace identifier and
        namespace-specific

        string allow applications to scope their endpoint IDs in a stable,

        globally unique way.
  schemas:
    GenericEventData:
      oneOf:
        - $ref: "#/components/schemas/MessageReceivedEventData"
        - $ref: "#/components/schemas/FileReceivedEventData"
        - $ref: "#/components/schemas/EndpointDeletedEventData"
        - $ref: "#/components/schemas/EndpointsListChangedEventData"
        - $ref: "#/components/schemas/AuthorizationAddedEventData"
        - $ref: "#/components/schemas/AuthorizationRevokedEventData"
      discriminator:
        propertyName: event_type
    PayloadURI:
      type: string
      format: uri
      description: >
        The URI to access the payload. May have hostname that is different from

        the API server, as payloads may be served from a different server or
        CDN.

        Clients MUST use provided URI as is without any modifications.

        If event embeds payload directly, this field would be absent.
    MessageReceivedEventData:
      type: object
      description: >
        Data structure for MESSAGE_RECEIVED events. This event would arrive
        whenever application

        got a message routed to one of its endpoints.
      required:
        - id
        - message_type
        - app_message_id
        - sent_at
        - event_type
        - receiving_endpoint_id
      properties:
        id:
          type: string
          format: uuid
          description: >
            The agrirouter message ID of the received message, generated by
            agrirouter.

            Use this value as `message_id` when confirming the message via `POST
            /confirmations`.
        app_message_id:
          type: string
          description: |
            The application message ID of the received message, generated by
            the sender. For payloads sent via the current API this is a UUID
            (see the `x-agrirouter-context-id` header on `POST /messages`),
            but messages from legacy senders may carry arbitrary strings —
            receivers MUST NOT assume UUID format here.
        message_type:
          type: string
          example: iso:11783:-10:taskdata:zip
          description: |
            The message type of the received message.
            See available types here:
            https://docs.agrirouter.com/agrirouter-interface-documentation/latest/tmt/overview.html
        sent_at:
          type: string
          format: date-time
          description: The timestamp when the message was sent by sending application.
        received_at:
          type: string
          format: date-time
          description: The timestamp when the message was received by agrirouter.
        payload:
          type: string
          format: byte
          description: The payload of the message, base64 encoded. Only one of `payload`
            or `payload_uri` would be present.
        payload_uri:
          $ref: "#/components/schemas/PayloadURI"
        event_type:
          type: string
          const: MESSAGE_RECEIVED
        receiving_endpoint_id:
          type: string
          format: uuid
          description: Internally-generated agrirouter ID of the receiving endpoint.
        filename:
          type: string
          description: Optional name of the file that is attached to messages as metadata.
        tenant_id:
          type: string
          description: The tenant ID of the to which receiving endpoint belongs. This is
            useful for confirming messages back to agrirouter.
        teamset_context_id:
          type: string
          description: The teamset context ID that was provided by sending application
            when sending the message, if any.
    EndpointDeletedEventData:
      type: object
      description: >
        Data structure for ENDPOINT_DELETED events. This event would arrive
        whenever one of the endpoints of the application is deleted from
        agrirouter, either by this API or by other means.
      required:
        - id
        - external_id
        - event_type
      properties:
        id:
          type: string
          format: uuid
          description: The agrirouter endpoint ID of the deleted endpoint.
        external_id:
          type: string
          description: The external ID of the deleted endpoint, which was provided when
            creating or updating the endpoint.
        event_type:
          type: string
          const: ENDPOINT_DELETED
    EndpointsListChangedEventData:
      type: object
      description: |
        Data structure for ENDPOINTS_LIST_CHANGED events. This event would
        arrive whenever the set of endpoints visible to the application, or
        their respective capabilities and/or routes, change in a tenant.

        ENDPOINTS_LIST_CHANGED is only emitted once the application has at
        least one endpoint of its own in the target tenant. Until that first
        application-owned endpoint exists, changes to other endpoints in the
        tenant are not reported, even if an authorization is in place. Once
        the first own endpoint is created, clients receive events for any
        subsequent change to the visible endpoint set.
      required:
        - event_type
        - tenant_id
        - endpoints
      properties:
        event_type:
          type: string
          const: ENDPOINTS_LIST_CHANGED
        tenant_id:
          type: string
          format: uuid
          description: The tenant whose endpoint list changed.
        endpoints:
          type: array
          description: |
            Complete current list of endpoints in the tenant that are visible
            to the application.
          items:
            $ref: "#/components/schemas/TenantEndpointInfo"
    AuthorizationAddedEventData:
      type: object
      description: |
        Data structure for AUTHORIZATION_ADDED events. This event would arrive
        whenever a user adds an authorization for a tenant to the current
        application.

        An authorization is uniquely identified by the triple
        (tenant_id, application_id, scope). The application_id is implicit from
        the receiving subscription; tenant_id and scope are explicit on this
        event. At present the only scope in use is `endpoints:manage`;
        additional scopes may be added in the future, and clients should then
        treat authorizations with different scopes as distinct authorizations
        even when tenant_id matches.
      required:
        - event_type
        - tenant
        - scope
      properties:
        event_type:
          type: string
          const: AUTHORIZATION_ADDED
        tenant:
          $ref: "#/components/schemas/TenantInfo"
        scope:
          type: string
          description: |
            The OAuth scope that was granted with this authorization. Today
            the only scope in use is `endpoints:manage`; additional scopes may
            be introduced in future revisions of this API.
          example: endpoints:manage
    AuthorizationRevokedEventData:
      type: object
      description: |
        Data structure for AUTHORIZATION_REVOKED events. This event would
        arrive whenever a user revokes an authorization for a tenant from the
        current application. When this event is delivered to you, you have
        already lost access to the target tenant for the given scope. You
        should clean up all information related to this tenant (for that scope)
        and update your state accordingly.

        All endpoints that were previously accessible via this authorization
        are considered removed. You will receive distinct ENDPOINT_DELETED
        events for all endpoints deleted as a result of the authorization
        revocation.

        Authorizations are uniquely identified by
        (tenant_id, application_id, scope); only the authorization matching the
        `scope` below was revoked. At present only the `endpoints:manage`
        scope is in use, but if additional scopes are introduced later, other
        scopes for the same tenant would remain in effect.
      required:
        - event_type
        - tenant_id
        - scope
      properties:
        event_type:
          type: string
          const: AUTHORIZATION_REVOKED
        tenant_id:
          type: string
          format: uuid
          description: The tenant whose authorization was revoked.
        scope:
          type: string
          description: |
            The OAuth scope of the authorization that was revoked. Today the
            only scope in use is `endpoints:manage`; additional scopes may be
            introduced in future revisions of this API.
          example: endpoints:manage
    FileReceivedEventData:
      type: object
      description: >
        Data structure for FILE_RECEIVED events. This event shall arrive
        whenever a big file transfer

        has completed in its entirety, i.e when big payload was sent as several
        message chunks, this

        event would be sent last when the complete payload is received.


        To read payload from this event when using the `payload_uri` field to
        download

        the complete payload, it is not required to provide Authorization for
        this

        request, as the URI itself contains access to that specific payload.


        These URIs are time-limited and would expire after at most 15 minutes,
        so clients 

        should download the payload before that happens.
      required:
        - event_type
        - receiving_endpoint_id
        - message_type
        - size
        - message_ids
      properties:
        payload_uri:
          $ref: "#/components/schemas/PayloadURI"
        payload:
          type: string
          format: byte
          description: |
            The payload of the file, base64 encoded. 
            Only one of `payload` or `payload_uri` can be present.
        event_type:
          type: string
          const: FILE_RECEIVED
        filename:
          type: string
          description: Optional name of the file that is attached to messages as metadata.
        receiving_endpoint_id:
          type: string
          format: uuid
          description: Internally-generated agrirouter ID of the receiving endpoint.
        message_type:
          type: string
          example: iso:11783:-10:taskdata:zip
          description: |
            The message type of the received payload.
            See available types here:
            https://docs.agrirouter.com/agrirouter-interface-documentation/latest/tmt/overview.html
        size:
          type: integer
          format: int64
          minimum: 0
          description: The size of file payload in bytes.
        message_ids:
          type: array
          items:
            type: string
            format: uuid
          description: >
            List of agrirouter message IDs of the messages that carried the file
            payload chunks.

            To confirm the file, all of these IDs must be confirmed via `POST
            /confirmations`,

            since a file may be a concatenation of chunks from the sender.
        tenant_id:
          type: string
          description: The tenant ID of the to which receiving endpoint belongs. This is
            useful for confirming messages back to agrirouter.
        teamset_context_id:
          type: string
          description: The teamset context ID that was provided by sending application
            when sending the message, if any.
    ConfirmMessagesRequest:
      type: object
      required:
        - confirmations
      properties:
        confirmations:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/MessageConfirmation"
          description: List of message confirmations.
    MessageConfirmation:
      type: object
      required:
        - message_id
        - endpoint_id
      properties:
        message_id:
          type: string
          format: uuid
          description: The agrirouter message ID of the confirmed message.
        endpoint_id:
          type: string
          format: uuid
          description: The agrirouter endpoint ID that received the message.
    EndpointsListResponse:
      type: object
      required:
        - endpoints
      properties:
        endpoints:
          type: array
          items:
            $ref: "#/components/schemas/TenantEndpointInfo"
    TenantsListResponse:
      type: object
      required:
        - tenants
      properties:
        tenants:
          type: array
          items:
            $ref: "#/components/schemas/TenantInfo"
    TenantInfo:
      type: object
      required:
        - tenant_id
        - endpoints
      properties:
        tenant_id:
          type: string
          format: uuid
          description: Authorized tenant ID.
        endpoints:
          type: array
          description: |
            Endpoints visible in this tenant, subject to the following
            privacy rule:

            - If the application has not yet created any endpoint of its own
              in this tenant, this array is empty — even if other endpoints
              exist in the tenant. This prevents exposing tenant contents to
              applications that have not yet actively participated in it.
            - Once the application has at least one of its own endpoints in
              the tenant, this array contains the full set of endpoints
              visible to the application.

            An authorized tenant with no application-owned endpoint will
            therefore still appear in `GET /tenants`, but with an empty
            `endpoints` array. See `owned_by_your_application` on
            `TenantEndpointInfo` for the distinction between own and other
            endpoints.
          items:
            $ref: "#/components/schemas/TenantEndpointInfo"
    TenantEndpointInfo:
      type: object
      required:
        - id
        - name
        - endpoint_type
        - application_id
        - tenant_id
        - owned_by_your_application
        - capabilities
      properties:
        id:
          type: string
          format: uuid
          description: The agrirouter endpoint ID.
        external_id:
          type: string
          description: External identifier of the endpoint, if available to the caller.
        name:
          type: string
          description: Display name of the endpoint.
        endpoint_type:
          $ref: "#/components/schemas/EndpointType"
        application_id:
          type: string
          format: uuid
          description: The ID of the application that owns the endpoint.
        tenant_id:
          type: string
          format: uuid
          description: The tenant ID of the endpoint.
        owned_by_your_application:
          type: boolean
          description: |
            Indicates whether this endpoint belongs to the application that is
            authorized for the current request.
        capabilities:
          $ref: "#/components/schemas/TenantEndpointCapabilities"
        routed_endpoints:
          $ref: "#/components/schemas/RoutedEndpoints"
    TenantEndpointCapabilities:
      type: object
      required:
        - can_send
        - can_receive
      properties:
        can_send:
          type: array
          description: Message types this endpoint can send.
          items:
            type: string
        can_receive:
          type: array
          description: Message types this endpoint can receive.
          items:
            type: string
    RoutedEndpoints:
      type: object
      description: |
        Route-derived information for this endpoint.

        This property only exists for endpoints owned by the authorized
        application. It is omitted for all other endpoints in the tenant.
      properties:
        can_send_to:
          $ref: "#/components/schemas/EndpointRouteMap"
        can_receive_from:
          $ref: "#/components/schemas/EndpointRouteMap"
    EndpointRouteMap:
      type: object
      description: |
        Map keyed by agrirouter endpoint ID. Each value lists the message types
        for which routing is currently possible between the two endpoints.
      additionalProperties:
        type: array
        items:
          type: string
          example: iso:11783:-10:taskdata:zip
    PutEndpointRequest:
      type: object
      required:
        - application_id
        - software_version_id
        - capabilities
        - subscriptions
        - endpoint_type
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
          pattern: ^[\p{L}\p{N} _.,:\-]+$
          description: >
            Optional name of the endpoint, for easier identification in
            agrirouter web interface.

            Does not have to be unique.

            If not specified, the name would be generated automatically.


            When provided, must be 1-200 characters long and may contain letters
            from any

            script, digits, spaces, and the following special characters: `-`,
            `_`, `.`, `,`, `:`. 

            Names consisting only of whitespace are not allowed as well, which
            is not

            expressed in the regex pattern.


            It is not guaranteed that this "application-set" name would be
            used, 

            because user may override it with "user-set" name in agrirouter web
            interface.

            Name send via this API cannot override "user-set" name, but

            it can update "application-set" name at any time and user can choose
            to

            switch name back to "application-set".
        application_id:
          type: string
          format: uuid
          description: The ID of the application that owns the endpoint
        software_version_id:
          type: string
          format: uuid
          description: The ID of the software version that owns the endpoint
        capabilities:
          type: array
          items:
            $ref: "#/components/schemas/EndpointCapability"
          description: The effective capabilities of the endpoint, must be subset of the
            capabilities of software version.
        subscriptions:
          type: array
          items:
            $ref: "#/components/schemas/EndpointSubscription"
        endpoint_type:
          $ref: "#/components/schemas/EndpointTypeToCreate"
        allow_delete_by_user:
          type: boolean
          default: false
          description: >
            Flag indicating whether the user is allowed to delete this endpoint.

            Note that even when this flag is not set, the user can still force
            deletion of the endpoint. Applications must handle the
            ENDPOINT_DELETED event on a best-effort basis. It is also possible
            that an endpoint is deleted immediately after creation due to a race
            with the user disconnecting the entire application, so applications
            should not rely on this flag to prevent endpoint deletion entirely.
        connections_uri:
          type: string
          format: uri
          description: >
            URI pointing to where the user can manage the entity connected to
            this endpoint, e.g. to disconnect or delete equipment from an
            equipment vendor. When provided, this URI will be shown when the
            user attempts to delete the endpoint, instead of the usual deletion
            dialog, directing them to the vendor's management page.
    Endpoint:
      type: object
      required:
        - id
        - external_id
        - endpoint_type
        - tenant_id
        - application_id
        - software_version_id
        - capabilities
      properties:
        id:
          type: string
          format: uuid
        external_id:
          type: string
        application_id:
          type: string
          format: uuid
        software_version_id:
          type: string
          format: uuid
        endpoint_type:
          $ref: "#/components/schemas/EndpointType"
        tenant_id:
          type: string
          description: The tenant ID of the endpoint
        capabilities:
          type: array
          items:
            $ref: "#/components/schemas/EndpointCapability"
        allow_delete_by_user:
          type: boolean
          default: false
          description: Flag indicating whether the user is allowed to delete this endpoint.
          readOnly: true
        connections_uri:
          type: string
          format: uri
          description: >
            URI pointing to where the user can manage the entity connected to
            this endpoint, e.g. to disconnect or delete equipment from an
            equipment vendor.
          readOnly: true
    EndpointSubscription:
      type: object
      required:
        - message_type
      properties:
        message_type:
          type: string
          example: iso:11783:-10:taskdata:zip
          description: |
            The message type that the endpoint is subscribed to.
            See available types here:
            https://docs.agrirouter.com/agrirouter-interface-documentation/latest/tmt/overview.html
    EndpointCapability:
      type: object
      required:
        - message_type
        - direction
      properties:
        message_type:
          type: string
          example: iso:11783:-10:taskdata:zip
          description: |
            The message type that the endpoint can send or receive.
            See available types here:
            https://docs.agrirouter.com/agrirouter-interface-documentation/latest/tmt/overview.html
        direction:
          type: string
          enum:
            - SEND
            - RECEIVE
            - SEND_RECEIVE
    ErrorResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          description: A human-readable error message describing what went wrong.
    EndpointType:
      type: string
      description: |
        Type of an endpoint that can be observed in the system. This includes
        types that can be created via this G4 API as well as legacy types that
        can only be created via the G2/G3 APIs. For values accepted when
        creating an endpoint, see `EndpointTypeToCreate`.
      enum:
        - cloud_software
        - virtual_communication_unit
        - communication_unit
        - telemetry_platform
    EndpointTypeToCreate:
      type: string
      description: |
        Type of an endpoint that can be created via this G4 API. Legacy types
        such as `communication_unit` and `telemetry_platform` cannot be
        created here; see `EndpointType` for the full set that may be observed.

        `farming_software` is accepted as a deprecated alias for
        `cloud_software` and will be removed in a future revision.
      enum:
        - cloud_software
        - virtual_communication_unit
        - farming_software
