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

# Event types

> Every event outbound webhooks can send, the envelope each one arrives in, the fields in its data and the JSON Schema that describes it, with a full example.

Each event type names something that happened in your workspace. You choose the types an endpoint receives when you set it up, and you can change them later.

Each event also has a reference page with every field, the signature headers and an example request: [app.created.v1](https://docs.base44.com/developers/references/outbound-webhooks/events/app-created-v1.md), [app.published.v1](https://docs.base44.com/developers/references/outbound-webhooks/events/app-published-v1.md), [app.unpublished.v1](https://docs.base44.com/developers/references/outbound-webhooks/events/app-unpublished-v1.md), [app.deleted.v1](https://docs.base44.com/developers/references/outbound-webhooks/events/app-deleted-v1.md), [app.restored.v1](https://docs.base44.com/developers/references/outbound-webhooks/events/app-restored-v1.md), [chat.turn\_started.v1](https://docs.base44.com/developers/references/outbound-webhooks/events/chat-turn-started-v1.md), [chat.turn\_ended.v1](https://docs.base44.com/developers/references/outbound-webhooks/events/chat-turn-ended-v1.md) and [webhook.test.v1](https://docs.base44.com/developers/references/outbound-webhooks/events/webhook-test-v1.md).

***

## The envelope

Every request body is a [CloudEvents 1.0](https://cloudevents.io/) JSON document, sent with `Content-Type: application/json`. The same attributes appear on every event, and `data` holds the fields for that event type.

| Attribute | Value |
| - | - |
| `specversion` | Always `1.0`. |
| `id` | The event's unique ID, such as `evt_68c1d2e3f4a5b6c7d8e9f0a1`. It's the same on every retry, so use it to ignore duplicates. |
| `source` | Your workspace, as `https://base44.com/workspaces/{workspace_id}`. |
| `type` | The event type, such as `app.created.v1`. |
| `subject` | What the event is about, as `apps/{app_id}`. |
| `time` | When the change happened, in UTC. |
| `datacontenttype` | Always `application/json`. |
| `dataschema` | The URL of the JSON Schema for `data`. |
| `traceparent`, `tracestate` | W3C trace context, only when present. |
| `authtype` | Who made the change: `user`, `api_key` (a workspace API key) or `system` (no person or key, for example a background job). Treat any other value as unknown. |
| `authid` | The ID of the user or API key that made the change. An empty string when `authtype` is `system`. |
| `data` | The event's own fields, listed below. |

Every timestamp, in `time` and in `data`, uses one format: `2026-09-01T10:15:30.123Z`.

***

## App events

These 5 events let you keep a copy of each app's state in your own system. Every one carries the same 3 identifying fields, plus a timestamp for the change.

| Field | Description |
| - | - |
| `app_id` | The app the event is about. |
| `workspace_id` | The workspace the app belongs to. |
| `owner_id` | The Base44 ID of the app's current owner. |

Events carry IDs only. App names, app content and personal details are never included.

| Event type | Sent when | Extra fields |
| - | - | - |
| `app.created.v1` | An app is created, including by cloning or importing. | `app_type`, `created_at` |
| `app.published.v1` | A version of an app goes live. | `published_version`, `published_at` |
| `app.unpublished.v1` | An app is taken offline, by a person or automatically (for example by a workspace policy or a plan change). | `unpublished_at` |
| `app.deleted.v1` | An app is moved to trash. | `deleted_at` |
| `app.restored.v1` | An app is restored from trash. | `restored_at` |

`app_type` is one of `user_app` (an app), `user_agent` (a Superagent), `user_game` (a game) or `imported_app` (an app imported from a repository). Treat `published_version` as an opaque string.

> [!WARNING]
>
> `app.deleted.v1` means the app moved to trash, not that it's gone. It can still be restored, and `app.restored.v1` tells you when it is. Don't delete your own copy of the app's data when you receive it.

### Example: app.created.v1

A complete request body for a new app:

```json theme={null}
{
  "specversion": "1.0",
  "id": "evt_68c1d2e3f4a5b6c7d8e9f0a1",
  "source": "https://base44.com/workspaces/68a1b2c3d4e5f6a7b8c9d0e1",
  "type": "app.created.v1",
  "subject": "apps/68b0c1d2e3f4a5b6c7d8e9f0",
  "time": "2026-09-01T10:15:30.123Z",
  "datacontenttype": "application/json",
  "dataschema": "https://base44.com/webhooks/schemas/app.created.v1.json",
  "authtype": "user",
  "authid": "6899aabbccddeeff00112233",
  "data": {
    "app_id": "68b0c1d2e3f4a5b6c7d8e9f0",
    "workspace_id": "68a1b2c3d4e5f6a7b8c9d0e1",
    "owner_id": "6899aabbccddeeff00112233",
    "app_type": "user_app",
    "created_at": "2026-09-01T10:15:30.123Z"
  }
}
```

***

## AI chat events

These 2 events tell you when the AI works on a message sent to an app's AI chat, so a platform that sends messages from its own server doesn't have to hold the request open or poll for the result. A turn is one message and everything the AI does in response.

Both carry the 3 identifying fields of the app events, plus:

| Field | Description |
| - | - |
| `message_id` | The message that started the turn: the ID returned when you sent it. Both events of a turn carry the same one. |
| `conversation_id` | The conversation the message belongs to. Left out when there isn't one. |
| `started_at` | When the AI started working on the message. |
| `continues_message_id` | Set when this turn continues one that was interrupted: the `message_id` of that turn. Follow it back to add up how long the whole response took. Left out otherwise. |

| Event type | Sent when | Extra fields |
| - | - | - |
| `chat.turn_started.v1` | The AI picks the message up. A message sent while another turn runs waits until then. | |
| `chat.turn_ended.v1` | The turn ends. Each turn ends once. | `status`, `ended_at` |

`status` is one of:

* `finished`: the AI replied and has nothing pending.
* `waiting_for_user_input`: the AI stopped to ask something, such as a clarifying question or a plan to approve. It continues when the next message answers it.
* `failed`: the turn hit an error.
* `cancelled`: the turn was stopped before it ended.
* `interrupted`: the AI was interrupted, for example by a restart. It continues in a new turn whose `continues_message_id` is this turn's `message_id`.

> [!NOTE]
>
> These events used to be called `builder.turn_started.v1` and `builder.turn_ended.v1`, with the same fields. Base44 now sends them only as `chat.turn_started.v1` and `chat.turn_ended.v1`, so an endpoint that still selects the old names receives nothing. To keep getting them, select the new names in Settings, where the old ones show as retired. With the API, call `PATCH /api/v1/outbound-webhooks/endpoints/{endpoint_id}` and pass the endpoint's full `selected_event_types` list, with the new names in place of the old ones. A list that still includes an old name is rejected. Then update any receiver code that checks `type` or `dataschema`.

***

## The activation test

Base44 sends `webhook.test.v1` when an endpoint is activated or tested. You can't select it, and it's sent to every endpoint that's being activated. Its `subject` is `endpoints/{endpoint_id}`.

| Field | Description |
| - | - |
| `endpoint_id` | The endpoint being activated. |
| `workspace_id` | The workspace the endpoint belongs to. |
| `sent_at` | When the test was sent. |
| `challenge` | Return this value in your response body to activate the endpoint. |

To learn how to answer it, see [Echoing the challenge](https://docs.base44.com/developers/references/outbound-webhooks/get-started/setting-up-endpoints.md#echoing-the-challenge).

***

## JSON Schemas

Each event type has a JSON Schema for its `data`, at the URL in the event's `dataschema` attribute:

| Event type | Schema |
| - | - |
| `app.created.v1` | [app.created.v1.json](https://base44.com/webhooks/schemas/app.created.v1.json) |
| `app.published.v1` | [app.published.v1.json](https://base44.com/webhooks/schemas/app.published.v1.json) |
| `app.unpublished.v1` | [app.unpublished.v1.json](https://base44.com/webhooks/schemas/app.unpublished.v1.json) |
| `app.deleted.v1` | [app.deleted.v1.json](https://base44.com/webhooks/schemas/app.deleted.v1.json) |
| `app.restored.v1` | [app.restored.v1.json](https://base44.com/webhooks/schemas/app.restored.v1.json) |
| `chat.turn_started.v1` | [chat.turn\_started.v1.json](https://base44.com/webhooks/schemas/chat.turn_started.v1.json) |
| `chat.turn_ended.v1` | [chat.turn\_ended.v1.json](https://base44.com/webhooks/schemas/chat.turn_ended.v1.json) |
| `webhook.test.v1` | [webhook.test.v1.json](https://base44.com/webhooks/schemas/webhook.test.v1.json) |

When an event's fields change in a way that could break your receiver, Base44 adds a new version, such as `app.created.v2`, and keeps sending the old version to endpoints that selected it for a support period.

> [!NOTE]
>
> The [Audit Logs API](https://docs.base44.com/developers/references/audit-logs-api/get-started/overview.md) has events with similar names, such as `app.created`. They're a separate record with their own fields, so the 2 can differ.


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