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

# Send a message

> Send a message to an agent, with files if you like, and get its reply in the format of OpenAI's Responses API.



## OpenAPI

````yaml api-reference/openapi.yaml POST /v1/responses
openapi: 3.0.3
info:
  title: QX API
  version: '1.0'
  description: >-
    Run what you build in QX from your own code. Message agents, execute flows
    and grids, and read their results.
servers:
  - url: https://api.qxlabs.com
security:
  - bearerAuth: []
tags:
  - name: Agents
    description: >-
      Message agents through OpenAI's Responses API, so OpenAI's SDKs work with
      a QX API key.
  - name: Flows
    description: Execute flows and follow their executions.
  - name: Grids
    description: Execute grids, follow their executions and read their rows.
paths:
  /v1/responses:
    post:
      tags:
        - Agents
      summary: Send a message
      description: >
        Sends a message to an agent, optionally with files, and returns the
        agent's turn in the format of OpenAI's Responses API, so OpenAI's SDKs
        work with a QX API key and this API's address.

        Without `previous_response_id` or `conversation`, the message starts a
        new conversation, which also shows in the app.

        With `stream: true`, the turn comes as server-sent events in the
        Responses API format. A call without streaming must finish within 240
        seconds, so stream long turns.

        In `normal` mode, a tool that needs approval pauses the turn with
        `mcp_approval_request` items. Answer them with `mcp_approval_response`
        items in `input`, on the same conversation and with no message.

        The turn uses your org's credits, as it would in the app.
      operationId: sendMessage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - model
                - input
              properties:
                model:
                  type: string
                  description: >-
                    The agent's ID, as shown in its URL in the app, or
                    `builtin:qx_agent` for the QX agent
                  example: builtin:qx_agent
                input:
                  description: >-
                    The message as text, or a list holding one user message, or
                    a list of answers to approval requests. Answers go on the
                    conversation that paused, with no message
                  oneOf:
                    - type: string
                      description: The message's text
                    - type: array
                      description: >-
                        One user message. A `system` or `developer` message
                        before it is skipped, since the agent's own instructions
                        apply
                      items:
                        $ref: '#/components/schemas/UserMessage'
                    - type: array
                      description: An answer to each approval request you want to settle
                      items:
                        $ref: '#/components/schemas/ApprovalResponse'
                  example: Summarise my unread emails from today
                previous_response_id:
                  type: string
                  description: Continues the conversation of this response
                conversation:
                  type: string
                  description: >-
                    Continues this conversation. Send this or
                    `previous_response_id`, not both
                stream:
                  type: boolean
                  default: false
                  description: Stream the turn as server-sent events
                permission_mode:
                  type: string
                  enum:
                    - delegated
                    - normal
                    - auto
                  description: >
                    What happens to a tool that needs approval, for this message
                    only.

                    `delegated` (the default for a message): it's refused.
                    `normal` (the default for answers): the turn pauses with
                    approval requests. `auto`: it runs without asking.
                connected_accounts:
                  type: object
                  additionalProperties:
                    type: string
                  description: >-
                    The account to use for an app on this message, as app to
                    account ID, from the connected accounts endpoint
            example:
              model: builtin:qx_agent
              input: Summarise my unread emails from today
              permission_mode: normal
              connected_accounts:
                gmail: ca_41f0c2d8
      responses:
        '200':
          description: >-
            The agent's turn. With `stream`, a stream of Responses API events
            instead
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    description: >-
                      The response's ID. Send it as `previous_response_id` to
                      continue the conversation
                  object:
                    type: string
                  created_at:
                    type: integer
                  status:
                    type: string
                    description: '`completed`, `failed` or `cancelled`'
                  model:
                    type: string
                  conversation:
                    type: object
                    properties:
                      id:
                        type: string
                        description: >-
                          The conversation's ID. Send it as `conversation` to
                          continue it, or to the conversation endpoint
                  previous_response_id:
                    type: string
                    nullable: true
                    description: The `previous_response_id` the request sent
                  output:
                    type: array
                    description: >-
                      What the agent did, in order. Its thinking as `reasoning`
                      items, its messages as `message` items, its finished tool
                      calls as `mcp_call` items, and `mcp_approval_request`
                      items when the turn paused
                    items:
                      $ref: '#/components/schemas/Item'
                  usage:
                    $ref: '#/components/schemas/Usage'
                  error:
                    type: object
                    nullable: true
                    description: Why the turn failed. Null otherwise
                    properties:
                      code:
                        type: string
                        enum:
                          - server_error
                      message:
                        type: string
                  steered:
                    type: boolean
                    description: >-
                      Only when the conversation was already running a turn. The
                      message joined that turn, which answers it
              example:
                id: resp_8d2c4e6f8a0b1c3d5e7f9a1b3c5d7e9f_4f1a2b3c4d5e
                object: response
                created_at: 1793714400
                status: completed
                model: builtin:qx_agent
                conversation:
                  id: 8d2c4e6f8a0b1c3d5e7f9a1b3c5d7e9f
                output:
                  - type: mcp_call
                    id: mcp_toolu_01A2b3C4d5E6
                    server_label: gmail
                    name: GMAIL_FETCH_EMAILS
                    arguments: '{}'
                    output: null
                    error: null
                    status: completed
                    approval_request_id: null
                  - type: message
                    id: msg_77c0a1b2c3d4
                    role: assistant
                    status: completed
                    content:
                      - type: output_text
                        text: You have 3 unread emails today...
                        annotations: []
                usage:
                  input_tokens: 5120
                  input_tokens_details:
                    cached_tokens: 0
                    cache_write_tokens: 0
                  output_tokens: 410
                  output_tokens_details:
                    reasoning_tokens: 120
                  total_tokens: 5530
                error: null
        '400':
          description: >-
            The request isn't valid JSON or isn't a valid Responses API request,
            a chosen account doesn't fit, or a file is empty, over 200 MB or of
            a type that isn't accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentsError'
              example:
                error:
                  message: >-
                    Send one user message. The conversation keeps the earlier
                    ones.
                  type: invalid_request_error
                  param: null
                  code: null
        '404':
          description: >-
            There's no such agent, conversation, response or approval request
            for you
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentsError'
        '409':
          description: >-
            The approval request was already handled, or the conversation is
            busy for an approval answer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentsError'
        '429':
          description: >-
            Rate limit exceeded, with code `rate_limit_exceeded`, or your org is
            out of credits, with code `insufficient_quota`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentsError'
              example:
                error:
                  message: >-
                    I can't reply right now because your organisation is out of
                    credits...
                  type: insufficient_quota
                  param: null
                  code: insufficient_quota
        '502':
          description: A file couldn't be read
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentsError'
      x-codeSamples:
        - lang: bash
          label: cURL
          source: |
            curl https://api.qxlabs.com/v1/responses \
              -H "Authorization: Bearer <YOUR_QX_API_KEY>" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "builtin:qx_agent",
                "input": "Summarise my unread emails from today",
                "permission_mode": "normal"
              }'
        - lang: python
          label: Python
          source: >
            from openai import OpenAI


            client = OpenAI(base_url="https://api.qxlabs.com/v1",
            api_key="<YOUR_QX_API_KEY>")


            response = client.responses.create(
                model="builtin:qx_agent",
                input="Summarise my unread emails from today",
                # QX's own fields go in extra_body
                extra_body={"permission_mode": "normal"},
            )

            print(response.output_text)
        - lang: typescript
          label: TypeScript
          source: >
            import OpenAI from "openai";


            const client = new OpenAI({ baseURL: "https://api.qxlabs.com/v1",
            apiKey: "<YOUR_QX_API_KEY>" });


            // QX's own fields aren't in OpenAI's types, so a call that sends
            them uses this type

            type Params = OpenAI.Responses.ResponseCreateParamsNonStreaming & {
              permission_mode?: "delegated" | "normal" | "auto";
              connected_accounts?: Record<string, string>;
            };


            const params: Params = {
              model: "builtin:qx_agent",
              input: "Summarise my unread emails from today",
              permission_mode: "normal",
            };

            const response = await client.responses.create(params);

            console.log(response.output_text);
components:
  schemas:
    UserMessage:
      type: object
      title: User message
      description: Your message. Its text and files go to the agent together
      required:
        - role
        - content
      properties:
        type:
          type: string
          enum:
            - message
          description: '`message`, the default when left out'
        role:
          type: string
          enum:
            - user
        content:
          description: The message's text, or its parts
          oneOf:
            - type: string
              title: Text
              description: The message's text
            - type: array
              description: >-
                Text, files and images, in any order. The texts join with a
                blank line between them
              items:
                $ref: '#/components/schemas/Part'
    ApprovalResponse:
      type: object
      title: Approval answer
      description: Your answer to one `mcp_approval_request` item. It covers that one call
      required:
        - type
        - approval_request_id
        - approve
      properties:
        type:
          type: string
          enum:
            - mcp_approval_response
        approval_request_id:
          type: string
          description: The approval request's `id`
          example: mcpr_6f1d2c3b4a5e6f708192a3b4_toolu_01A2b3C4d5E6
        approve:
          type: boolean
          description: Whether the call runs
    Item:
      type: object
      title: Item
      description: >-
        One thing in a turn. `type` says which kind it is, and the other fields
        go with it
      properties:
        type:
          type: string
          enum:
            - reasoning
            - message
            - mcp_call
            - mcp_approval_request
          description: >-
            `reasoning` for the agent's thinking, `message` for a message,
            `mcp_call` for a tool call, `mcp_approval_request` for a call
            waiting for your approval
        id:
          type: string
          description: >-
            An approval request's ID goes back as `approval_request_id` in your
            answer
          example: msg_77c0a1b2c3d4
        status:
          type: string
          enum:
            - in_progress
            - completed
            - failed
            - incomplete
          description: >-
            For reasoning, messages and tool calls. A tool call that failed is
            `failed`, and one that was stopped is `incomplete`
        role:
          type: string
          enum:
            - assistant
            - user
          description: >-
            For messages. `assistant` for the agent's, `user` for yours, which
            only a conversation's items hold
        content:
          type: array
          description: >-
            For messages. The agent's message is an `output_text` part with its
            text and the sources it cites. Your message is an `input_text` part,
            then an `input_file` part per file
          items:
            type: object
            properties:
              type:
                type: string
                enum:
                  - output_text
                  - input_text
                  - input_file
              text:
                type: string
                description: For `output_text` and `input_text`
              filename:
                type: string
                description: For `input_file`
              annotations:
                type: array
                description: >-
                  For `output_text`. A `url_citation` for each source with a
                  link
                items:
                  type: object
                  properties:
                    type:
                      type: string
                      enum:
                        - url_citation
                    url:
                      type: string
                    title:
                      type: string
                    start_index:
                      type: integer
                      description: >-
                        Where the citation's `[n]` marker starts in the text. 0
                        when the text doesn't cite it
                    end_index:
                      type: integer
                      description: Where the marker ends. 0 when the text doesn't cite it
        summary:
          type: array
          description: For reasoning. The thinking as `summary_text` parts
          items:
            type: object
            properties:
              type:
                type: string
                enum:
                  - summary_text
              text:
                type: string
        server_label:
          type: string
          description: >-
            For tool calls and approval requests. `qx` for one of QX's own
            tools, or the app, like `gmail`
        name:
          type: string
          description: >-
            For tool calls and approval requests. The tool, like `web_search` or
            `GMAIL_SEND_EMAIL`
        arguments:
          type: string
          description: >-
            For tool calls and approval requests, as a JSON object in a string.
            A call to one of QX's own tools shows only the `command`, `query`,
            `url` or `path` it was given, with a command cut to 40 characters,
            and a call to an app's tool shows `{}`. An approval request has the
            call's arguments in full, with the agent's `reason` for it among
            them
          example: '{}'
        output:
          type: string
          nullable: true
          description: For tool calls. Always null, since a tool's result stays in the app
        error:
          type: string
          nullable: true
          description: >-
            For tool calls. The result when the call failed or was stopped, null
            otherwise
        approval_request_id:
          type: string
          nullable: true
          description: >-
            For tool calls. The approval request the call ran after, if it
            needed one
    Usage:
      type: object
      description: The turn's tokens. Null while the turn runs
      nullable: true
      properties:
        input_tokens:
          type: integer
        input_tokens_details:
          type: object
          properties:
            cached_tokens:
              type: integer
              description: Always 0
            cache_write_tokens:
              type: integer
              description: Always 0
        output_tokens:
          type: integer
        output_tokens_details:
          type: object
          properties:
            reasoning_tokens:
              type: integer
              description: The thinking's share of `output_tokens`
        total_tokens:
          type: integer
    AgentsError:
      type: object
      description: OpenAI's error shape
      properties:
        error:
          type: object
          properties:
            message:
              type: string
            type:
              type: string
            param:
              type: string
              nullable: true
            code:
              type: string
              nullable: true
    Part:
      type: object
      title: Part
      description: >-
        A text, file or image. `type` says which, and the other fields go with
        it
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - input_text
            - input_file
            - input_image
        text:
          type: string
          description: For `input_text`
        filename:
          type: string
          description: >-
            For `input_file`. The file's name with its extension, which tells
            the agent its type
          example: report.pdf
        file_data:
          type: string
          description: >-
            For `input_file`. The file as a base64 data URL, up to 200 MB. A
            type the app doesn't accept gets a 400
          example: data:application/pdf;base64,JVBERi0xLjQK...
        image_url:
          type: string
          description: >-
            For `input_image`. The image as a base64 data URL. It reaches the
            agent as a file named `image-1`, `image-2` and so on, with the data
            URL's type as its extension
          example: data:image/png;base64,iVBORw0KGgo...
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Your QX API key. The `X-API-Key` header works too

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.