> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reflecto.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Poll an interaction for the phone's answer

> Returns the current state of an interaction created via `POST /v1/interactions`. `wait` requests a **bounded server-side wait**: the call holds the connection open and returns early as soon as the phone answers, or once the wait budget is spent — whichever comes first — so a single poll can replace a naive tight loop. Cross-principal reads and unknown ids are indistinguishable — both return `404` rather than confirming an id exists to the wrong owner.



## OpenAPI

````yaml /openapi.yaml get /v1/interactions/{id}
openapi: 3.1.0
info:
  title: Reflecto API
  version: 1.1.0
  description: >-
    The Reflecto public HTTP API lets you push notifications to your own paired

    devices — your Android phone and any browser extensions you've paired with
    it.

    Designed for the same job as Pushover or the old Pushbullet API: a single

    HTTPS call from a script, cron job, or webhook puts a notification on every

    screen you own.


    Authentication uses opaque prefixed bearer tokens (`rfk_live_…`) created and

    managed on your paired Android device — there are no email accounts, no

    passwords, no server-side user records beyond device pairing state. See the

    Authentication guide for the token lifecycle.


    The public-API surface is small on purpose: `POST /v1/send`, its

    capability-URL alias `POST /v1/send/{token}`, and a Pushover-compatible shim

    at `POST /v1/messages.json`. Mirroring traffic from your phone to your

    extension never reaches this surface — it stays end-to-end encrypted via the

    private `/v1/sync` SSE stream.


    See the Guides tab for the quickstart, authentication model, rate-limit

    contract, and encryption posture.
  contact:
    name: Reflecto
    url: https://github.com/reflectoapp/reflecto
  license:
    name: MIT
    url: https://github.com/reflectoapp/reflecto/blob/main/LICENSE
servers:
  - url: https://api.reflecto.dev
    description: Production
security:
  - BearerAuth: []
tags:
  - name: Send
    description: Push notifications to one or all of your paired devices.
  - name: Pushover compatibility
    description: >-
      Drop-in replacement for `POST https://api.pushover.net/1/messages.json`.
      Field names, request shape, and response shape mirror Pushover's so
      existing senders can migrate by changing a single URL. See [Pushover
      compatibility guide](/guides/pushover-compatibility) for the full mapping.
paths:
  /v1/interactions/{id}:
    get:
      tags:
        - Interactions
      summary: Poll an interaction for the phone's answer
      description: >-
        Returns the current state of an interaction created via `POST
        /v1/interactions`. `wait` requests a **bounded server-side wait**: the
        call holds the connection open and returns early as soon as the phone
        answers, or once the wait budget is spent — whichever comes first — so a
        single poll can replace a naive tight loop. Cross-principal reads and
        unknown ids are indistinguishable — both return `404` rather than
        confirming an id exists to the wrong owner.
      operationId: getInteraction
      parameters:
        - schema:
            type: string
            description: The interaction id returned by `POST /v1/interactions`.
            example: int_3f6e2a7c9b1d4f8e8a5b6c1d2e3f4a5b
          required: true
          description: The interaction id returned by `POST /v1/interactions`.
          name: id
          in: path
        - schema:
            type: integer
            description: >-
              Seconds to hold the connection open waiting for an answer —
              bounded server-side wait, returns early when answered.
              Out-of-range values are clamped into [0, 45].
            example: 20
            default: 0
            minimum: 0
            maximum: 45
          required: false
          description: >-
            Seconds to hold the connection open waiting for an answer — bounded
            server-side wait, returns early when answered. Out-of-range values
            are clamped into [0, 45].
          name: wait
          in: query
      responses:
        '200':
          description: Current interaction state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PollInteractionResponse'
              examples:
                pending:
                  summary: Still pending
                  value:
                    id: int_3f6e2a7c9b1d4f8e8a5b6c1d2e3f4a5b
                    status: pending
                    expires_at: 1754800000
                answered:
                  summary: Answered
                  value:
                    id: int_3f6e2a7c9b1d4f8e8a5b6c1d2e3f4a5b
                    status: answered
                    choice_index: 0
                    answered_at: 1754799500
                    expires_at: 1754800000
                expired:
                  summary: Expired, unanswered
                  value:
                    id: int_3f6e2a7c9b1d4f8e8a5b6c1d2e3f4a5b
                    status: expired
                    expires_at: 1754800000
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimitedInteractions'
      security:
        - BearerAuth: []
components:
  schemas:
    PollInteractionResponse:
      type: object
      properties:
        id:
          type: string
          description: Interaction id, echoed back.
          example: int_3f6e2a7c9b1d4f8e8a5b6c1d2e3f4a5b
        status:
          type: string
          enum:
            - pending
            - answered
            - expired
          description: Current interaction state.
          example: answered
        choice_index:
          type: integer
          minimum: 0
          maximum: 3
          description: >-
            Index into the original `choices` array. Present only once `status`
            is `answered`.
          example: 0
        answered_at:
          type: integer
          description: >-
            Unix epoch seconds when the phone answered. Present only once
            `status` is `answered`.
          example: 1754799500
        expires_at:
          type: integer
          description: Unix epoch seconds when the interaction expires (or expired).
          example: 1754800000
      required:
        - id
        - status
        - expires_at
    SimpleErrorResponse:
      type: object
      properties:
        error:
          type: string
          example: invalid_token
        message:
          type: string
      required:
        - error
      description: >-
        Flat error shape used for auth failures — `invalid_token` carries only
        the stable code; `missing_token` adds a `message` hint pointing at the
        `Authorization: Bearer …` header. The `message` field is therefore
        optional.
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: message_too_long
            message:
              type: string
              example: message must be ≤ 1500 bytes
            details:
              type: object
              additionalProperties: {}
          required:
            - code
            - message
      required:
        - error
  responses:
    Unauthorized:
      description: Missing, invalid, or revoked bearer token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SimpleErrorResponse'
          examples:
            missingToken:
              value:
                error: missing_token
                message: 'Authorization: Bearer rfk_live_… required'
            invalidToken:
              value:
                error: invalid_token
    Forbidden:
      description: Token exists but lacks scope for the requested action.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            priorityCapped:
              value:
                error:
                  code: priority_capped
                  message: Token's priority_cap is 'default'; requested 'high'
    NotFound:
      description: >-
        The requested resource does not exist or is not visible to this
        principal.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            notFound:
              summary: >-
                Unknown interaction id, one past its 24h retention window, or
                owned by a different principal — cross-principal reads are
                deliberately indistinguishable from unknown ids (never confirm
                existence to the wrong owner)
              value:
                error:
                  code: not_found
                  message: not_found
    RateLimitedInteractions:
      description: >-
        Rate limit or resource cap exceeded. For interaction creation this
        includes the 10-pending-per-owner cap; for ring and reminders, IP/token
        rate limits.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          examples:
            rateLimitExceeded:
              value:
                error:
                  code: rate_limit_exceeded
                  message: Rate limit hit
            tooManyPending:
              summary: Owner already has 10 pending interactions
              value:
                error:
                  code: too_many_pending
                  message: too_many_pending
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: rfk_live_…
      description: >-
        Opaque bearer token created on your paired Android phone. The format is
        `rfk_live_` followed by 32 URL-safe alphanumeric characters (≥160 bits
        of entropy). Test tokens use the `rfk_test_` prefix.

````