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

# Generate Answer

> Send a message event into Fini and return the public events created for that submission.

Use this endpoint when your backend needs to send a turn into Fini. Pass `interactionId` to continue an existing conversation, or `botId` to let Fini create or resolve one for that agent. The route stores the incoming event, runs the normal reply flow for `user` messages, and returns the public event objects created while handling that submission.

<Tip>
  Use [List agents](/en/api-reference/list-agents) to get a `botId`. This route returns only the created event array, not the full conversation wrapper. Use [Get conversation](/en/api-reference/get-conversation) when you already know the conversation ID, or [List conversations](/en/api-reference/list-conversations) when you need to discover or export it.
</Tip>

## Headers

<ParamField header="Authorization" type="string" required>
  Bearer token containing your Fini workspace API key. Format: `Bearer fini_...` The key needs `write` scope.
</ParamField>

## Body parameters

<ParamField body="content" type="string" required>
  Message text to add as the incoming event.
</ParamField>

<ParamField body="role" type="string" default="user">
  Event role. Allowed values are `user`, `agent`, `finibot`, and `otherbot`. Most public callers should use `user`.
</ParamField>

<ParamField body="interactionId" type="string">
  Existing conversation ID to continue. If omitted, provide `botId` and Fini will create or resolve a conversation for that bot.
</ParamField>

<ParamField body="botId" type="string">
  Agent ID to use when starting a new conversation. Required when `interactionId` is omitted.
</ParamField>

<ParamField body="metadata" type="object">
  Optional conversation metadata to merge into the request state.

  <Expandable title="metadata properties">
    <ParamField body="metadata.user_attributes" type="object">
      User attributes to merge into the conversation metadata for this submission.
    </ParamField>

    <ParamField body="metadata.channel" type="string">
      Optional channel override. Allowed values are `chat` and `email`.
    </ParamField>
  </Expandable>
</ParamField>

<Note>
  Public attachment upload support for `Generate Answer` is coming soon through a dedicated public API flow. Until that ships, treat this route as text-only.
</Note>

## Request example

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://api-prod.usefini.com/v2/hc-interactions/events/public' \
    --header 'Authorization: Bearer fini_your_api_key' \
    --header 'Content-Type: application/json' \
    --data '{
      "botId": "4f5ef695-d03b-4d56-8fef-7f2bd5c17ef3",
      "role": "user",
      "content": "Where is my order?",
      "metadata": {
        "user_attributes": {
          "email": "customer@example.com"
        },
        "channel": "chat"
      }
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api-prod.usefini.com/v2/hc-interactions/events/public",
      headers={
          "Authorization": "Bearer fini_your_api_key",
          "Content-Type": "application/json",
      },
      json={
          "botId": "4f5ef695-d03b-4d56-8fef-7f2bd5c17ef3",
          "role": "user",
          "content": "Where is my order?",
          "metadata": {
              "user_attributes": {"email": "customer@example.com"},
              "channel": "chat",
          },
      },
  )
  events = response.json()
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    "https://api-prod.usefini.com/v2/hc-interactions/events/public",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer fini_your_api_key",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        botId: "4f5ef695-d03b-4d56-8fef-7f2bd5c17ef3",
        role: "user",
        content: "Where is my order?",
        metadata: {
          user_attributes: { email: "customer@example.com" },
          channel: "chat",
        },
      }),
    }
  );
  const events = await response.json();
  ```
</RequestExample>

<Note>
  If you send `role: "agent"` or another non-user role, Fini stores that event and returns it immediately. The route only generates a new AI reply for `user` events.
</Note>

## Response

The response is a top-level array of public events created while handling this submission. This is the same event shape returned inside `events[]` on [List conversations](/en/api-reference/list-conversations).

<ResponseField name="[]" type="array">
  Array of public events created for the submission.

  <Expandable title="PublicEvent object">
    <ResponseField name="id" type="string">
      Event ID.
    </ResponseField>

    <ResponseField name="createdAt" type="integer">
      Event creation time in Unix epoch milliseconds.
    </ResponseField>

    <ResponseField name="role" type="string">
      Event role such as `user`, `finibot`, `agent`, or `otherbot`.
    </ResponseField>

    <ResponseField name="type" type="string">
      Event type such as `message`, `internalnote`, `no_reply`, `silent_escalation`, `debounce`, or `widget_form`.
    </ResponseField>

    <ResponseField name="message" type="string | null">
      Message content stored on the event, when present.
    </ResponseField>

    <ResponseField name="externalId" type="string | null">
      Provider-side message ID when available.
    </ResponseField>

    <ResponseField name="externalCreatedAt" type="integer | null">
      Provider-side timestamp in Unix epoch milliseconds when available.
    </ResponseField>

    <ResponseField name="csatRating" type="number | null">
      Numeric CSAT value attached to the event, when present.
    </ResponseField>

    <ResponseField name="feedback" type="string | null">
      Free-text feedback note stored on the event, when present.
    </ResponseField>

    <ResponseField name="approved" type="boolean | null">
      Thumbs up (`true`), thumbs down (`false`), or unrated (`null`).
    </ResponseField>

    <ResponseField name="resolved" type="boolean | null">
      Resolution flag stored on the event, when present.
    </ResponseField>

    <ResponseField name="attachments" type="array">
      File attachments on the event. When present, signed attachment URLs are refreshed before the response is returned.

      <Expandable title="attachment object">
        <ResponseField name="gcpUrl" type="string">
          Current signed URL for downloading the attachment.
        </ResponseField>

        <ResponseField name="gcpPath" type="string">
          Internal storage path for the attachment.
        </ResponseField>

        <ResponseField name="originalUrl" type="string">
          Original source URL recorded for the attachment.
        </ResponseField>

        <ResponseField name="contentType" type="string">
          MIME type of the attachment.
        </ResponseField>

        <ResponseField name="expiresAt" type="integer">
          Unix epoch milliseconds when the signed download URL expires.
        </ResponseField>

        <ResponseField name="sizeBytes" type="integer">
          File size in bytes when available.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="tags" type="array">
      Tags attached to the event.

      <Expandable title="tag object">
        <ResponseField name="id" type="string">
          Tag ID.
        </ResponseField>

        <ResponseField name="name" type="string">
          Tag name.
        </ResponseField>

        <ResponseField name="groupId" type="string | null">
          Parent tag-group ID when one exists.
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="usedArticles" type="array">
      Public article references retrieved for that event.

      <Expandable title="article object">
        <ResponseField name="id" type="string">
          Article ID.
        </ResponseField>

        <ResponseField name="title" type="string">
          Article title.
        </ResponseField>

        <ResponseField name="documentUrl" type="string | null">
          Source document URL when available.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseExample>
  ```json 200 OK theme={null}
  [
    {
      "id": "cc0e1592-4b71-4f57-bf9c-1b10a6b7b71a",
      "createdAt": 1749640953000,
      "role": "user",
      "type": "message",
      "message": "Where is my order?",
      "externalId": null,
      "externalCreatedAt": null,
      "csatRating": null,
      "feedback": null,
      "approved": null,
      "resolved": null,
      "attachments": [],
      "tags": [],
      "usedArticles": []
    },
    {
      "id": "da370d54-5d2a-4b38-b99d-8b5c5f77bff7",
      "createdAt": 1749640955000,
      "role": "finibot",
      "type": "message",
      "message": "I can help with that. What is your order number?",
      "externalId": null,
      "externalCreatedAt": null,
      "csatRating": null,
      "feedback": null,
      "approved": null,
      "resolved": null,
      "attachments": [],
      "tags": [],
      "usedArticles": [
        {
          "id": "7f5392e5-dc7d-4558-8860-cf3ea4b32f94",
          "title": "Track your order",
          "documentUrl": null
        }
      ]
    }
  ]
  ```

  ```json 401 Unauthorized theme={null}
  {
    "statusCode": 401,
    "message": "Invalid or revoked API key",
    "error": "Unauthorized"
  }
  ```

  ```json 403 Forbidden theme={null}
  {
    "statusCode": 403,
    "message": "API key does not have the required scope for this operation",
    "error": "Forbidden"
  }
  ```
</ResponseExample>

## Errors

<AccordionGroup>
  <Accordion title="400 Bad Request" icon="circle-exclamation">
    The body is malformed. Common causes are omitting `content`, or omitting both `interactionId` and `botId`.
  </Accordion>

  <Accordion title="401 Unauthorized" icon="lock">
    The API key is missing, malformed, revoked, or invalid. Confirm you are sending `Authorization: Bearer fini_...` with the full key.
  </Accordion>

  <Accordion title="403 Forbidden" icon="shield-halved">
    The API key does not include the `write` scope required for this route.
  </Accordion>

  <Accordion title="500 Internal Server Error" icon="triangle-exclamation">
    Fini failed while resolving the bot, loading or creating the conversation, persisting the event, or generating the reply. Unknown `interactionId` or `botId` values currently surface here on the public route as well. Retry once, then investigate the conversation in [Inbox](/en/testing/inbox) if the error persists.
  </Accordion>
</AccordionGroup>
