---
title: "POST /feedback_portal/{portalId}/widget/{widgetId}/events"
method: POST
path: "/feedback_portal/{portalId}/widget/{widgetId}/events"
tags: ["Feedback Portals"]
---

# POST /feedback_portal/{portalId}/widget/{widgetId}/events

`POST /feedback_portal/{portalId}/widget/{widgetId}/events`

## Request

- Base URL: `https://api.craft.io`
- URL: `https://api.craft.io/feedback_portal/{portalId}/widget/{widgetId}/events`
- Auth: API key in header `x-api-key`

Tell Craft that the widget was opened, or submitted, on your page.

**Fire and forget.** `202` with no body, and the body means nothing beyond "received". Do not
retry it, do not wait for it before showing your form, and never let it fail a submission — a
relay that breaks the thing it measures is worse than no measurement.

**What to send.** `eventName` is `beWidgetOpened` (the form was shown to somebody) or
`beWidgetSubmitted`. Everything else is optional context about the page:

```json
{ "eventName": "beWidgetOpened", "widgetVersion": "v1",
  "browser": "<navigator.userAgent>", "mobile": false, "categoriesSize": 3 }
```

`portalId` and `widgetId` may also be sent and must then match the path, which is what counts.

**Nothing about the person reaches Craft through this endpoint** — no page URL, nothing typed
into the form, no identity. An unlisted property is a `422` rather than a silent drop, so a typo
is something you see on your first attempt instead of a series that never arrives.

Takes the same three credentials as the config read: a widget token, a development key, or an
account API key.

## Path parameters

- `portalId` string, required
- `widgetId` string, required

## Request body

- WidgetEventRequest: One thing the embedded widget did on a customer's page, for Craft to emit as a BI event. **Fire and forget.** The response is `202` with no body and carries no meaning beyond "received"; ignore it, do not retry it, and never let it delay or fail a submission. Nothing here changes anything a person can see. **What Craft never receives through this endpoint**: the page's URL, anything the submitter typed, their identity, and any property not listed below — an unlisted key is a `422`, so a typo is something you find on your first attempt rather than a series that silently never arrives. (`sourceUrl` on the create routes is how page context reaches a *submission*; this is not it.)
  - `eventName` string, required: What happened. **`beWidgetOpened`** — the form was shown to somebody. **`beWidgetSubmitted`** — they submitted it. Anything else is a `422` listing the two.
  - `portalId` string: Optional, and must match the portal in the path when sent. The path is what counts.
  - `widgetId` string: Optional, and must match the widget in the path when sent.
  - `widgetVersion` string: The widget bundle's own version, e.g. `v1`.
  - `browser` string: The browser's user-agent string, up to 256 characters.
  - `mobile` boolean: Whether it was drawn on a phone-sized screen.
  - `categoriesSize` number, double: How many categories this widget offered on this open.

## Response `202`

Received — no body, and nothing to retry

## Other responses

- `401`: Missing, expired or invalid credential
- `403`: Access denied, or this portal is not set up for widgets
- `422`: Unknown eventName, an unlisted property, or an id that disagrees with the path
- `429`: The portal's widget rate limit — carries `Retry-After`

## Changes

- **2026-10-01** `6009bb084cb5`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/craft/apis/craft-io-public-api/changes/feedback_portal/:portalId/widget/:widgetId/events/post.md)

---

[API](https://skmtc.dev/craft/apis/craft-io-public-api.md) - [All operations](https://skmtc.dev/craft/apis/craft-io-public-api/llms.txt) - [OpenAPI document](https://skmtc.dev/craft/apis/craft-io-public-api/revisions/2972a1b05ab1?raw)
