openapi: 3.0.3
info:
  title: EPU4 SDK API
  version: 4.0.0
  description: 'Interactive API reference for the EPU SDK, based on the **EPU SDK - API Integration Guide** (October
    19, 2026).


    **Workflow:** log in with your EPU secret and device ID using `POST /user_login`; pass the returned Bearer token
    to `POST /derive` and `GET /mdad`. To update MDAD, send **only `text`** in a `/derive` request. This updates
    the EPU without an OpenAI call, and broadcasts an updated `mdad` event to connected Socket.IO clients. Add optional
    `reasoning: true` to request an AI explanation or `action: true` for a suggested robot action.


    **Realtime updates:** connect to the Socket.IO namespace `https://emohuman.com/<epuid>` and listen for `mdad`.
    `GET /mdad` waits for the next MDAD frame (within approximately five seconds) and returns an MDAD numeric array.
    The October 19 guide describes 120 values; a live EPU4 REST read on October 31, 2026 returned 123. Check the
    actual length.


    **Current gateway session policy:** by default, the Bearer session expires after 60 minutes without an authenticated
    REST API request; each authenticated REST request renews the inactivity timer. An optional login `duration`
    (minutes) changes this default. Receiving Socket.IO MDAD events does not renew the REST session. The Bearer
    token has a separate maximum validity of 24 hours. An opt-in `GET /mdad` around 55 minutes of inactivity can
    keep an intentional long-running session active, but can consume an HTTP cached MDAD frame; never enable keepalive
    silently for abandoned clients. If a protected request returns HTTP 401, obtain a new token. Socket.IO may remain
    connected without a valid REST session. See `x-metasoul-session` below.


    **Source limitations:** the original integration guide does not specify detailed HTTP error status codes, token
    expiry, CORS policy, or the semantic meaning of individual MDAD values. The session policy above describes the
    deployed gateway implementation, not guarantees of that original guide.


    **MDAD decoding:** The companion `mdad-channels.json` and the `x-metasoul-mdad` extension below document the
    12 apex indexes and Optimistic (index 9) from the *EPU-III Brochure*, pp. 14-15. This is separate from the EPU4
    integration guide, which specifies the array length but does not individually define its channels. A live EPU4
    response contained indexes 120-122 (123 total values), but index 123 was absent. The legacy interpretation of
    those extra indexes is not yet verified against EPU4 firmware.


    **Agent instructions:** https://emohuman.com/docs/EPU4/agent-guide.md'
servers:
- url: https://emohuman.com
  description: EPU4 service
tags:
- name: Authentication
  description: Exchange EPU device credentials for a session token.
- name: Emotion processing
  description: Send text to update MDAD, optionally request AI reasoning or a robot action.
- name: MDAD
  description: Read the latest streamed numeric MDAD array (at least 120; 123 observed live).
- name: Account inventory
  description: Read-only account EPUID listing using one account secret; no EPU login or device operation.
paths:
  /user_login:
    post:
      tags:
      - Authentication
      summary: Log in and obtain a session token
      operationId: userLogin
      description: The bundled EPU4 documentation has a **Connect to EPU4** panel that calls this endpoint and automatically
        applies the returned Bearer token to protected requests. In other OpenAPI viewers, call this endpoint and
        enter the returned `token` using **Authorize**. The returned `epuid` identifies the Socket.IO namespace.
        The current gateway defaults to 60 minutes of REST inactivity; optional `duration` specifies a different
        timeout in minutes. Token validity is separately capped at 24 hours.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
            example:
              secret: YOUR_EPU_SECRET
              epuid: YOUR_EPU_ID
      responses:
        '200':
          description: Successful login (example from integration guide).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginSuccess'
              example:
                reply: ok
                token: abc123...
                epuid: your_epu_id
        default:
          $ref: '#/components/responses/UnspecifiedError'
  /derive:
    post:
      tags:
      - Emotion processing
      summary: Update MDAD from text (optional AI reasoning or action)
      operationId: deriveEmotion
      description: 'Send a situation or message with **`text` only** to make the EPU process it and update the MDAD
        **without an OpenAI call**. Every `/derive` call broadcasts an updated MDAD frame to connected Socket.IO
        clients. Both `reasoning` and `action` are **optional**. Set `reasoning: true` if you want an AI-generated
        explanation, or `action: true` for a suggested robot action. A text-only request normally responds with
        `{"reply":"ok"}`; no AI-generated `text` field is required.'
      security:
      - BearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeriveRequest'
            examples:
              send:
                summary: Send text to update MDAD (no AI call)
                value:
                  text: I am walking alone at night in a dark street
              reasoning:
                summary: Explain emotion result
                value:
                  text: I am walking alone at night in a dark street
                  reasoning: true
              action:
                summary: Suggest robot action
                value:
                  text: I am walking alone at night in a dark street
                  action: true
              timezone:
                summary: Reasoning with timezone
                value:
                  text: I am walking alone at night in a dark street
                  reasoning: true
                  timezone: America/New_York
      responses:
        '200':
          description: 'Successful request: text-only mode returns reply ok; optional AI modes can also include
            generated text.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeriveSuccess'
              examples:
                send:
                  summary: Text-only MDAD update
                  value:
                    reply: ok
                reasoning:
                  summary: Reasoning result
                  value:
                    reply: ok
                    text: I chose Fear because walking alone at night is a life-threatening situation...
                action:
                  summary: Robot action result
                  value:
                    reply: ok
                    text: First action would be to scan surroundings and find a safe exit route
        default:
          $ref: '#/components/responses/UnspecifiedError'
  /mdad:
    get:
      tags:
      - MDAD
      summary: Receive the next MDAD array
      operationId: getMdad
      description: Retrieves MDAD for the authenticated EPU. The endpoint has **no request body**. Per the guide,
        it waits for the next MDAD streamed in from the EPU (within approximately **5 seconds**), then returns a
        numeric MDAD array. The 2026 source guide describes 120 values; a live REST response contained 123 on October
        31, 2026. Validate the actual length. An authenticated GET renews the gateway's inactivity timer, so an
        opt-in keepalive may call GET /mdad after 55 minutes without other authenticated REST activity. The call
        can consume a cached HTTP frame; it does not generate an emotion or consume the Socket.IO subscription.
        A 503 EPU_NOT_CONNECTED means no new MDAD frame was available and does not by itself indicate an expired
        token. The original integration guide did not specify timeout response details.
      security:
      - BearerAuth: []
      responses:
        '200':
          description: 'Numeric MDAD array: at least 120 values; a live response contained 123.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MdadSuccess'
              example:
                reply: ok
                mdad:
                - 0
                - 0
                - 1
                - 1
                - 2
                - 1
                - 1
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 0
                - 51
                - 49
                - 50
        default:
          $ref: '#/components/responses/UnspecifiedError'
  /epuid_list:
    post:
      tags:
      - Account inventory
      operationId: listAccountEpuids
      summary: EPUID List for one account secret
      description: Read-only account inventory. Send a single MetaSoul account secret, without an EPUID, Bearer
        token or device login. The gateway calls its existing `POST /get_secret_info` integration once and returns
        **only** validated EPUID lists grouped as `available`, `in_use` and `availability_unknown`. No raw account
        response, account metadata or secret is returned. The upstream nested `avl`/`resrved` grouping comes from
        the existing population-initialization client; it is not an independently verified live availability or
        reservation contract. A flat response is explicitly classified `availability_unknown`. The endpoint does
        not select, reserve, log in to, initialize or operate an EPU. Full identifiers and account membership are
        sensitive; avoid logging responses. Requires server route registration, rate limiting, and an explicit `EPUID_LIST_ENABLED=1`
        deployment setting. Do not use until deployed; HTTP 503 means the feature is disabled.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AccountSecretRequest'
            example:
              secret: YOUR_ACCOUNT_SECRET
      responses:
        '200':
          description: Validated account EPUID inventory with no raw upstream fields.
          headers:
            Cache-Control:
              description: no-store
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountEpuidListResponse'
        '400':
          description: Missing, malformed or unexpected request fields.
        '502':
          description: Upstream account inventory unavailable, rejected, ambiguous or invalid.
        '503':
          description: EPUID listing is disabled on the gateway.
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: In the bundled EPU4 interactive docs, the Connect panel fills this automatically. For other OpenAPI
        viewers, use the `token` returned by `POST /user_login` (without the `Bearer` prefix). The default gateway
        inactivity limit is 60 minutes; Socket.IO MDAD frames do not extend it. The token also has a maximum lifetime
        of 24 hours.
  schemas:
    LoginRequest:
      type: object
      additionalProperties: false
      required:
      - secret
      - epuid
      properties:
        secret:
          type: string
          minLength: 1
          description: Your EPU secret key.
          example: YOUR_EPU_SECRET
        epuid:
          type: string
          minLength: 1
          description: Your EPU device ID.
          example: YOUR_EPU_ID
        duration:
          type: number
          minimum: 0
          exclusiveMinimum: true
          description: Optional REST inactivity timeout in minutes; defaults to 60 if omitted.
          example: 60
    LoginSuccess:
      type: object
      required:
      - reply
      - token
      - epuid
      properties:
        reply:
          type: string
          example: ok
          description: '`ok` on success.'
        token:
          type: string
          description: Use this value in the Authorization Bearer header. The JWT has a 24-hour maximum lifetime
            even if authenticated REST activity renews the idle session.
          example: abc123...
        epuid:
          type: string
          description: EPU device ID returned by the server.
    DeriveRequest:
      type: object
      required:
      - text
      description: Only `text` is required. Omit `reasoning` and `action` to update MDAD without an AI-generated
        response; set either to `true` only when needed.
      properties:
        text:
          type: string
          minLength: 1
          description: Situation or message to analyze.
          example: I am walking alone at night in a dark street
        reasoning:
          type: boolean
          description: Optional. Set true to request an AI-generated explanation; omit for a text-only MDAD update.
        action:
          type: boolean
          description: Optional. Set true to request a suggested robot action; omit for a text-only MDAD update.
        timezone:
          type: string
          description: Optional client timezone, e.g. `America/New_York`.
          example: America/New_York
    DeriveSuccess:
      type: object
      required:
      - reply
      properties:
        reply:
          type: string
          example: ok
          description: '`ok` on success.'
        text:
          type: string
          description: Optional AI-generated explanation or action; not present for a text-only MDAD update.
    MdadSuccess:
      type: object
      required:
      - reply
      - mdad
      properties:
        reply:
          type: string
          example: ok
          description: '`ok` on success.'
        mdad:
          type: array
          minItems: 120
          description: Numeric MDAD array. The October 19, 2026 EPU4 guide describes 120 values, but a live October
            31 EPU4 GET /mdad returned 123; do not enforce an exact length. The first 120 contain the historical
            emotion/feeling layout (APEX indexes 4,14,...,114; Optimistic 9). Indexes 120-122 were present in that
            live response; their EPU-III meanings are not yet independently confirmed on EPU4. Index 123 was absent.
          items:
            type: number
    ErrorReply:
      type: object
      description: 'Only `reply: "error"` is indicated in the guide; any further fields and HTTP status are unspecified.'
      properties:
        reply:
          type: string
          example: error
      additionalProperties: true
    AccountSecretRequest:
      type: object
      additionalProperties: false
      required:
      - secret
      properties:
        secret:
          type: string
          minLength: 1
          maxLength: 4096
          writeOnly: true
          format: password
          description: Account-wide MetaSoul secret; use HTTPS and never record or print this value.
          example: YOUR_ACCOUNT_SECRET
    AccountEpuidListResponse:
      type: object
      additionalProperties: false
      required:
      - reply
      - total
      - available
      - in_use
      - availability_unknown
      properties:
        reply:
          type: string
          enum:
          - ok
        total:
          type: integer
          minimum: 1
          description: Total distinct EPUIDs in this account inventory.
        available:
          type: array
          items:
            type: string
            pattern: ^[A-F0-9]{32}$
          description: EPUIDs listed in the upstream avl group.
        in_use:
          type: array
          items:
            type: string
            pattern: ^[A-F0-9]{32}$
          description: EPUIDs listed in the upstream resrved/reserved group.
        availability_unknown:
          type: array
          items:
            type: string
            pattern: ^[A-F0-9]{32}$
          description: EPUIDs from flat legacy inventory responses with no reliable status grouping.
  responses:
    UnspecifiedError:
      description: Error (the integration guide does not specify HTTP error statuses or additional fields).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorReply'
          example:
            reply: error
externalDocs:
  description: MetaSoul EPU4 bilingual interactive docs and live MDAD monitor
  url: https://emohuman.com/docs/EPU4/
x-metasoul-mdad:
  arrayIndexing: zero-based
  documentedEPU4ArrayLength: 120
  layoutSource: EPU-III Brochure, pages 14-15 (distinct from EPU4 API guide)
  apexIndexFormula: 4 + 10 * emotionIndex
  emotionApexChannels:
  - emotion: Excite
    channel: 4
  - emotion: Confident
    channel: 14
  - emotion: Happy
    channel: 24
  - emotion: Trust
    channel: 34
  - emotion: Desire
    channel: 44
  - emotion: Fear
    channel: 54
  - emotion: Surprise
    channel: 64
  - emotion: Inattention
    channel: 74
  - emotion: Sad
    channel: 84
  - emotion: Nostalgia
    channel: 94
  - emotion: Disgust
    channel: 104
  - emotion: Anger
    channel: 114
  optimisticFeelingChannel: 9
  legacyAdditionalChannels:
  - name: Pulse speed
    channel: 120
    interpretation: 0 slower; 50 normal; 100 faster
  - name: Frustration / Satisfaction
    channel: 121
    interpretation: 0 maximum satisfaction / relief of frustration; 50 neutral; 100 maximum frustration
  - name: Pain / Pleasure
    channel: 122
    interpretation: 0 maximum pleasure / relief of pain; 50 neutral; 100 maximum pain
  - name: Temperature
    channel: 123
    interpretation: EPU-III brochure lists a range of -40 to +85 °C
  warning: The October 19 source guide documented 120 values, but one live EPU4 REST read on October 31 returned
    123. Validate the actual payload length. Indexes 120-122 were present in that read; their EPU-III interpretations
    are not yet verified for EPU4. Index 123 (historical temperature) was absent.
  fullChannelMap: https://emohuman.com/docs/EPU4/mdad-channels.json
  observedLiveEPU4ArrayLengths:
  - 123
  observationDate: '2026-10-31'
x-metasoul-socket-io:
  type: Socket.IO
  connectionBaseUrl: https://emohuman.com
  namespaceTemplate: /{epuid}
  epuidSource: Use epuid returned by POST /user_login
  receiveEvent: mdad
  payload:
    epuid: string
    mdad: number[] (at least 120; 123 observed via REST)
  trigger: A POST /derive request, including a text-only request, triggers a broadcast
  security: The integration guide does not describe Socket.IO authentication; do not assume REST Bearer token is
    needed for the socket.
  reference: https://emohuman.com/docs/EPU4/agent-guide.md
x-metasoul-session:
  scope: Describes the current gateway implementation; original integration guide did not specify token policy.
  defaultInactivityTimeoutMinutes: 60
  configurableLoginDurationMinutes: true
  maximumJwtLifetimeHours: 24
  inactivityRenewal: An authenticated REST API request refreshes the session's last-used time.
  socketIoEventsRenewInactivity: false
  optionalKeepalive:
    method: GET
    path: /mdad
    afterMinutesWithoutRestActivity: 55
    automaticByDefault: false
    caution: GET /mdad can consume an HTTP cached frame or wait for a new one. Do not keep abandoned EPUIDs reserved.
  sessionFailure: HTTP 401 means the REST session is invalid or expired; obtain a new token.
  noMdadFrame: HTTP 503 EPU_NOT_CONNECTED can occur after successful authentication and is not a token expiry.
  disconnectCaution: The Swagger Disconnect button clears browser state but does not immediately release the upstream
    EPU reservation.
