> Doc v2.3 · Updated 2026-09-06 · AEvent MCP server "AEvent" · 72-tool consumer surface

# AEvent MCP Tools Reference

This is the complete reference for the AEvent consumer tool surface (72 tools) exposed by the AEvent MCP server. It is written for developers and AI agents that drive AEvent over the Model Context Protocol.

Every tool, parameter table, example call, and return shape in this document is drawn from the live AEvent tool surface. Write/mutating tools are shown with their call shape only. Example IDs and names are generic placeholders; resolve real IDs in your own account before calling.

---

## How to read this reference

### Endpoint and transport

The AEvent MCP server is reached at:

```
https://app-api.aevent.com/mcp
```

Transport is streamable HTTP. This is a real MCP server: point any MCP client (Claude, an Agents SDK runtime, etc.) at that URL and it will negotiate over MCP. The endpoint advertises the `mcp-protocol-version` header. The server identifies itself as `"AEvent"`.

### Authentication

Auth is a single HTTP header:

```
Authorization: Bearer <TOKEN>
```

The token carries the `mcp:use` scope. An unauthenticated request returns HTTP 401 with header `www-authenticate: Bearer realm="mcp", error="invalid_token"`.

> Token provisioning. Generate your token inside the AEvent app at **Settings > Developer**: create a token, check the **`mcp:use`** scope, click Create, and copy it (you cannot read it again). Use it as `Authorization: Bearer <TOKEN>`.

### Account targeting (optional)

Your Bearer token already scopes every call to the account that owns it, so a normal customer connection passes NEITHER of the params below. They exist only for operator / multi-account contexts (for example, a proxy that operates several accounts behind one connection):

| Param | Type | Notes |
|-------|------|-------|
| `email` | string | Email address of the account to act on behalf of. |
| `tenant_id` | string | AEvent tenant ID for the account to act on behalf of. |

For a normal token-scoped connection, omit both. The example calls below omit them too.

### The campaign ID (`wtl`)

Most campaign-scoped tools take `wtl`:

> `wtl` (a.k.a. `webinarTimeline`) is the campaign ID: a 15-character system-generated identifier (for example `aaa111bbb222ccc`). Never construct it from a campaign name. Always resolve it with `list-campaigns` first.

### The "required" caveat (important)

The live JSON schemas mark NOTHING as `required`. There is no `required` array on any tool. Functional requirements (for example, `wtl` on action builders, `url` on page setters) are enforced server-side and described in prose, not in the schema. In every parameter table below, the "Required" column states the SERVER-ENFORCED / functional requirement, not what the raw schema says (the raw schema says everything is optional).

### Shared action-builder params

The message/automation builders (`add-email`, `add-text-message`, `add-autochat`, `add-voice-mail`, `add-tag`, `remove-tag`, `add-to-list`, `add-additional-event`, `add-c-t-a-popup`, `add-messenger-message`, `add-webhook-action`) share a standard quartet:

| Param | Type | Notes |
|-------|------|-------|
| `audience` | string | Audience name (e.g. `Registrants`, `Attendees`, `Non-Attendees`) or ID to target. Leave blank for all registrants. Combine with `and` / `or`. Resolve via `list-audiences`. |
| `eventID` | string | Which additional event to target; blank = main event. Combine with `and` / `or`. Resolve via `list-multi-events`. |
| `integrationID` | string | Which connected integration to use; blank = auto-match. Resolve via `list-campaign-integrations`. |
| `time` | string | Relative-time string, e.g. `"1 day 3 hours 30 minutes after"` or `"1 day before at 3:00pm"`, relative to the event. |

### Discover-before-act

The golden rule for agents: resolve real IDs before any write. In order:

1. `list-campaigns` -> `wtl`
2. `list-campaign-integrations` (or `list-integrations`) -> `integrationID`
3. `list-audiences` -> `audience` (name or id)
4. `list-multi-events` -> `eventID`
5. `list-videos` / `list-audio` / `list-images` / `list-nifty-images` -> file IDs

Then call the write tool. Never infer an ID from a name.

---

## Key concepts an agent must hold

- **Campaign** (a.k.a. `wtl` / `webinarTimeline`): the container. Holds settings, the timeline, registration, pages, integrations, audiences, and multi-events. ID like `aaa111bbb222ccc`.
- **Webinar**: a scheduled instance of a campaign (a dated occurrence). Has its own integer instance `id` (used by `show-event-info`), distinct from `wtl`.
- **Timeline**: the sequence of actions (emails, SMS/MMS, autochat, ringless voicemail, tags, list adds, webhooks) that live INSIDE a campaign. Built by the `add-*` tools.
- **Integrations**: connected services (Twilio, SendGrid/SMTP, GoHighLevel, ActiveCampaign, Zoom, GoToWebinar, calendars, webhooks, etc.).
- **Audiences**: segments. Every campaign has three defaults: **Registrants**, **Attendees**, **Non-Attendees**. Actions accept the audience name or its ID. Campaigns may add custom behavioral segments. (An ID, if you need one, is obtainable from `general.userGroup`.)
- **Multi-Events**: additional events (Day 2, encore, post-event) fanned out from a source event/audience.
- **Format** (read from `general.webinarType`): `automatic` = like-live, `semi-automatic` = hybrid, `manual` = live. Always read the field, do not infer format from the campaign title.

---

# TOOL CATALOG

Tools are grouped by function. Within each group: purpose, parameter table, an example call, and an example return shape.

Example calls omit account-targeting params (your token scopes the account). All IDs and names in examples are generic placeholders; resolve real values in your own account first.

---

## 1. Campaign Management

### list-campaigns

List or search campaigns. Returns campaign name keyed by `wtl`.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | no | Campaign name or partial. Omit to list all. |
| `email` / `tenant_id` | string | one of | Account target. |

Example call:

```json
{ "tool": "list-campaigns" }
```

Example return (trimmed):

```json
{
  "aaa111bbb222ccc": "My Webinar",
  "ddd333eee444fff": "Multi-Day Masterclass",
  "ggg555hhh666iii": "Demo Webinar",
  "jjj777kkk888lll": "Product Launch Webinar",
  "mmm999nnn000ooo": "AStream Demo",
  "ppp111qqq222rrr": "5-Day Challenge"
}
```

### create-campaign

Create a campaign. Returns the new campaign (creates its own `wtl`, so no `wtl` input).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `name` | string | yes | Campaign name. |
| `integrationID` | string | yes | Integration to use with this campaign. |
| `options` | array | no | String modifiers (see below; not schema-enforced, value-validated server-side). |
| `email` / `tenant_id` | string | one of | Account target. |

`options` accepted values (description-only enum, supply correct values): `obvio`, `zoom`, `aevent`, `gotowebinar`, `youtube`, `oneTime`, `multiOption`, `multiSession`, `highDefinition`, `standardDefinition`, `hybrid`, `likeLive`, `live`.

Example:

```json
{ "tool": "create-campaign",
  "name": "Demo Like-Live", "integrationID": "aevent",
  "options": ["aevent", "likeLive", "oneTime", "highDefinition"] }
```

### show-campaign

Dump campaign settings and timeline actions by section. For troubleshooting/read use.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `section` | string | no | One of `actions`, `general`, `registration`, `autoresponder`, `integration`, `page` (description-only enum). |
| `email` / `tenant_id` | string | one of | Account target. |

Example call:

```json
{ "tool": "show-campaign", "wtl": "<your-campaign-id>", "section": "general" }
```

Example `general` (key fields):

```json
{
  "timelineID": "aaa111bbb222ccc",
  "campaign_name": "My Webinar",
  "webinarType": "manual",
  "manualLength": "120",
  "webinarVideoLength": 7200,
  "replay": { "enabled": true, "replayDays": "3", "expireOnMid": 1 },
  "userGroup": [
    { "id": "3",  "name": "Registrants" },
    { "id": "a1", "name": "Attendee" },
    { "id": "a2", "name": "Non-Attendee" }
  ],
  "timelineName": "My Webinar"
}
```

`section: "actions"` returns timeline actions bucketed by day-key (`"0"` = event day, `"+1"` = 1 day after, `"-1"` = event-day-minus). A real SMS action from the `"0"` bucket:

```json
{
  "id": "_p3q4r5s6t",
  "action": "twilio-textuser",
  "actionTitle": "Send SMS",
  "groupID": "3", "groupName": "Registrants",
  "isAfter": 1, "isExact": false, "minutes": "15",
  "integrationID": "tw0sms0integ0001", "integrationType": "TWILIO",
  "model": {
    "sms-text": "{{!subscriber-firstName!}}, if you're not on here, you're missing out... Join NOW: {{!subscriber-joinURL!}}",
    "phonenumber": "+15551234567"
  }
}
```

Timing read: `isAfter:1 / isExact:false / minutes:15` in day-key `"0"` = "15 minutes after" the start. This is what the `time` param on the `add-*` builders compiles into.

---

## 2. Webinar Scheduling

### schedule-webinar

Schedule a webinar instance for a campaign.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `integrationID` | string | yes | Connected delivery integration to use. |
| `timestamp` | integer | yes | Start time as a UNIX timestamp (seconds). |
| `recurring` | string | no (nullable) | The recurring-schedule condition. Supplying it makes `schedule-webinar` CREATE a recurring schedule (an evergreen cadence) rather than a one-shot instance. Format below. |
| `email` / `tenant_id` | string | one of | Account target. |

The `recurring` string is a pipe-delimited (`|`) string with exactly 13 positions (0-indexed). This is the same spec `edit-recurring` uses; the two entries are kept consistent:

```
isRecurring | startTime | endTime | lengthMINUTES | period | interval | weekdays | startDate | expiration | mode | encoreTimeline | encoreTimestamp | timezone
```

- **period** (field 5) = `DAILY` / `WEEKLY` / `HOURLY` / `MINUTE` / `ONCE`.
- **weekdays** (field 7) = comma-separated day names; required for `WEEKLY`.
- **expiration** (field 9) = `NEVER`, an end date (`MM/DD/YYYY`), or a number of occurrences.
- **mode** (field 10) enum = `WEB`, `WEBINAR`, `MEETING`, `CLASSIC`, `BROADCAST`, `OBVIO`, or `YOUTUBE`.
- **encoreTimeline / encoreTimestamp** (fields 11/12) = blank unless this is an encore.
- **timezone** (field 13) = an IANA name, or `REGISTRANT` for an evergreen (registrant-timezone or page-load-relative) schedule.

Evergreen variants:
- Registrant-timezone evergreen: keep a clock time in `startTime`, put the number of days after registration in `endTime`, and use `REGISTRANT` as the timezone. Example: `TRUE|3:00PM|0|100MINUTES|ONCE|1||8/21/2026|NEVER|WEB|||REGISTRANT`.
- Page-load-relative evergreen: put a whole number of seconds from page load in `startTime`, `RELATIVE` in `endTime`, `ONCE` in period, a blank `startDate`, and `REGISTRANT` as the timezone. `startTime` is never a clock time and never carries units. Example (15 minutes from page load): `TRUE|900|RELATIVE|100MINUTES|ONCE|1|||NEVER|WEB|||REGISTRANT`.

Example `recurring` value:

```
TRUE|06:00PM|06:00PM|100MINUTES|WEEKLY|1|MONDAY|08/22/2023|NEVER|MEETING|||America/New_York
```

Example one-shot:

```json
{ "tool": "schedule-webinar", "wtl": "<your-campaign-id>",
  "integrationID": "aevent", "timestamp": 1781020800 }
```

Example recurring:

```json
{ "tool": "schedule-webinar", "wtl": "<your-campaign-id>",
  "integrationID": "aevent", "timestamp": 1781020800,
  "recurring": "TRUE|06:00PM|06:00PM|100MINUTES|WEEKLY|1|MONDAY|08/22/2023|NEVER|MEETING|||America/New_York" }
```

### edit-webinar

Move a scheduled webinar to a different time. Only webinars that have not started yet may be edited.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `id` | integer | yes | The `webinarID` of the event to edit (from `list-upcoming-webinars`). |
| `timestamp` | integer | yes | The new start time as a UNIX timestamp (seconds). |
| `wtl` | string | no | Campaign ID. |
| `experienceType` | string | no (nullable) | Optionally convert between the Zoom `MEETING` and `WEBINAR` experience. Defaults to the current experience. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "edit-webinar", "id": 123456, "timestamp": 1781020800 }
```

### edit-recurring

Change the recurring condition of a recurring entry, and optionally move the webinars it already created. Dangerous: it can reshape a live evergreen cadence.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `recurringID` | integer | yes | The `uniqueID` of the recurring entry to edit (from `list-recurring`). |
| `recurring` | string | yes | The new recurring condition, pipe-delimited with exactly 13 fields (below). |
| `wtl` | string | no | Campaign ID. |
| `timestamp` | integer | no (nullable) | Optionally move the next webinar this entry created to this UNIX timestamp (seconds). Webinars further in the future are moved by the same amount. Omit to leave already-scheduled webinars in place. |
| `email` / `tenant_id` | string | one of | Account target. |

The `recurring` string has exactly 13 pipe-delimited fields (identical to `schedule-webinar`):

```
isRecurring | startTime | endTime | lengthMINUTES | period | interval | weekdays | startDate | expiration | mode | encoreTimeline | encoreTimestamp | timezone
```

- **period** = `DAILY` / `WEEKLY` / `HOURLY` / `MINUTE` / `ONCE`.
- **weekdays** = comma-separated day names; required for `WEEKLY`.
- **expiration** = `NEVER`, an end date, or a number of occurrences.
- **mode** = `WEB`, `WEBINAR`, `MEETING`, `CLASSIC`, `BROADCAST`, `OBVIO`, or `YOUTUBE`.
- **encoreTimeline / encoreTimestamp** = blank unless this is an encore.
- **timezone** = an IANA name, or `REGISTRANT` for evergreen.

Evergreen variants:
- Registrant-timezone evergreen: keep a clock time in `startTime`, put the number of days after registration in `endTime`, and use `REGISTRANT` as the timezone. Example: `TRUE|3:00PM|0|100MINUTES|ONCE|1||8/21/2026|NEVER|WEB|||REGISTRANT`.
- Page-load-relative evergreen: put a whole number of seconds from page load in `startTime`, `RELATIVE` in `endTime`, `ONCE` in period, a blank `startDate`, and `REGISTRANT` as the timezone. `startTime` is never a clock time and never carries units. Example (15 minutes from page load): `TRUE|900|RELATIVE|100MINUTES|ONCE|1|||NEVER|WEB|||REGISTRANT`.

Example:

```json
{ "tool": "edit-recurring", "wtl": "<your-campaign-id>", "recurringID": 12345,
  "recurring": "TRUE|12:00PM|12:00PM|100MINUTES|DAILY|1||8/21/2026|NEVER|WEB|||America/New_York" }
```

### list-upcoming-webinars

List upcoming scheduled webinar instances.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | no | Filter by campaign. |
| `page` | integer | no | 100 per page, default 1. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-upcoming-webinars", "wtl": "<your-campaign-id>" }
```

Example return: `[]` (nothing scheduled). An empty array is the normal "nothing scheduled" response, not an error.

### list-past-webinars

List past webinar instances.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | no | Filter by campaign. |
| `page` | integer | no | 100 per page, default 1. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-past-webinars", "wtl": "<your-campaign-id>" }
```

Example return: `[]`.

### list-recurring

List recurring entries that regularly create webinars and keep the webinar dropdown filled.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | no | Filter by campaign. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-recurring", "wtl": "<your-campaign-id>" }
```

Example return: `[]`. Operational note: an AEvent "archive" deletes recurring entries and unarchive does not restore them, so an empty `list-recurring` on an otherwise-configured campaign can indicate a prior archive.

### list-schedules

List scheduled actions (the per-occurrence queued action entries).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `webinarID` | string | no | Filter by webinar ID. |
| `occurrenceID` | string | no | Filter by occurrence ID. |
| `actionType` | string | no | Filter by schedule action type, e.g. `addTag`. |
| `onQueue` | string | no | Filter by queue name, e.g. `gohighlevel` for GoHighLevel add-tag actions. |
| `search` | string | no | Additionally filter by strings in the `param` column, e.g. the tag name. |
| `page` | integer | no | 100 per page, default 1. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-schedules", "webinarID": "<your-webinar-id>", "actionType": "addTag" }
```

Return is an array of queued schedule rows. Each row has these columns (verified live): `uniqueID` (integer), `uuid` (string, `<webinarID>-<actionID>`), `onQueue` (queue name, e.g. `webinar` / `twilio` / `system`), `webinarID`, `param` (a JSON STRING holding the full action model), `action` (e.g. `livechat`, `textuser`, `attendee`), `executeAt` (UNIX seconds), `created_at`, `updated_at`, `isCancelled` (0/1), `parent`, `occurrenceID`. A trimmed row:

```json
{
  "uniqueID": 6045512,
  "uuid": "84250092939-_lqRAP3nfV",
  "onQueue": "twilio",
  "webinarID": "84250092939",
  "param": "{\"id\":\"_lqRAP3nfV\",\"action\":\"twilio-textuser\",\"sms-text\":\"...\",\"phonenumber\":\"service\",\"integrationID\":\"...\",\"groupID\":[\"a1\"]}",
  "action": "textuser",
  "executeAt": 1788717600,
  "created_at": "2026-09-06T03:00:29.000000Z",
  "updated_at": "2026-09-06T03:50:31.000000Z",
  "isCancelled": 0,
  "parent": null,
  "occurrenceID": null
}
```

The `search` param matches strings inside the `param` column (e.g. a tag name); `actionType`, `onQueue`, `webinarID`, `occurrenceID` filter the other columns.

### pre-schedule-guard

Run read-only pre-flight checks before scheduling a webinar: the hidden 10-minute delivery-platform overlap buffer and the manual-control early-truncation trap. Returns actionable warnings. Nothing is scheduled.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `timestamp` | integer | no | Proposed start time as a UNIX timestamp (seconds). Required to check the overlap buffer; omit to run only the manual-control check. |
| `integrationID` | string | no | The delivery-platform integration to schedule on. Omit to use the campaign's configured delivery platform. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "pre-schedule-guard", "wtl": "<your-campaign-id>",
  "integrationID": "<your-delivery-integration-id>", "timestamp": 1781020800 }
```

### show-event-info

Show event information (attendance rate, date, etc.) for a single webinar instance.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `id` | integer | yes | The webinar INSTANCE id (from `list-upcoming-webinars` / `list-past-webinars`), NOT the `wtl`. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "show-event-info", "id": 123456 }
```

---

## 3. Integrations

### list-integrations

List all account integrations. Returns the integration name + type + `connected` flag keyed by integrationID.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | no | Integration name or partial. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-integrations" }
```

Real return (trimmed):

```json
{
  "ac0crm0integ0001": { "name": "ACTIVECAMPAIGN", "type": "ACTIVECAMPAIGN", "connected": true },
  "tw0sms0integ0001": { "name": "TWILIO", "type": "TWILIO", "connected": true },
  "sg0email0integ01": { "name": "My SMTP", "type": "SMTP", "connected": true },
  "ghl0crm0integ001": { "name": "GOHIGHLEVEL", "type": "GOHIGHLEVEL", "connected": true },
  "zm0webinar0integ": { "name": "ZOOM", "type": "ZOOM", "connected": true },
  "aevent":          { "name": "ASTREAM", "type": "AEVENT", "connected": true }
}
```

Writer notes: `connected: true` is SHALLOW. It does not validate the OAuth token or plan depth (a known trap). An account can hold multiple records of the same type (e.g. several ZOOM or GMAIL records). Always resolve the exact `integrationID` per campaign via `list-campaign-integrations`, never by type alone.

### list-campaign-integrations

List integrations connected to a specific campaign. Returns integration name keyed by integrationID.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-campaign-integrations", "wtl": "<your-campaign-id>" }
```

Real return:

```json
{ "tw0sms0integ0001": "TWILIO", "ac0crm0integ0001": "ACTIVECAMPAIGN" }
```

### connect-integration

Connect a credential-only (paste-key) integration such as ActiveCampaign, Twilio, ManyChat, ConvertKit, or Kartra by supplying its raw credentials. Validate the credentials first with `check-integration-credential`. OAuth integrations (Zoom, HubSpot, Google) cannot be connected here because they require a browser consent step.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `type` | string | yes | The integration type to connect, e.g. `ACTIVECAMPAIGN`, `TWILIO`, `MANYCHAT`, `CONVERTKIT`, `KARTRA`. Must be a credential-only (paste-key) type. |
| `credentials` | object | yes | The paste-key credential fields for the type. Field names vary: ActiveCampaign needs `api_key` + `api_url`; Twilio needs `sid` + `token`; ConvertKit needs `api_key` + `api_secret`; ManyChat needs `api_key`, `api_url`, `referenceID`. |
| `name` | string | no | Display name for the integration. Defaults to the type. |
| `numbers` | array | no | For Twilio or Telnyx only: the phone numbers to attach to the auto-created Messaging Service, e.g. `["+15551234567"]`. Omit for other types. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "connect-integration", "type": "ACTIVECAMPAIGN",
  "credentials": { "api_key": "<your-api-key>", "api_url": "https://youracct.api-us1.com" },
  "name": "My ActiveCampaign" }
```

### check-integration-credential

Validate a paste-key integration credential before connecting it, so a bad key fails fast instead of committing a broken connection. Returns whether the credentials are valid.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `type` | string | yes | The integration type to validate, e.g. `ACTIVECAMPAIGN`, `TWILIO`, `MANYCHAT`, `CONVERTKIT`, `KARTRA`. |
| `credentials` | object | yes | The paste-key credential fields for the type. ActiveCampaign needs `api_key` + `api_url`; Twilio needs `sid` + `token`; ConvertKit needs `api_key` + `api_secret`. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "check-integration-credential", "type": "TWILIO",
  "credentials": { "sid": "<your-twilio-sid>", "token": "<your-twilio-token>" } }
```

### test-integration

Test whether an integration is connected and working.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `integrationID` | string | yes | Integration to test. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "test-integration", "integrationID": "<your-sms-integration-id>" }
```

Caveat: like `connected: true`, a "connected" result is a SHALLOW check. It does not validate token/plan depth.

### list-integration-resources

List the pickable resources of a connected integration (lists, tags, forms, numbers, automations, workflows, channels, hooks) so an action can target one. Returns a normalized array of `{id, name, type, extra}`.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `integrationID` | string | yes | The integration to enumerate resources for (from `list-integrations`). |
| `resourceType` | string | no | Return only one resource type, e.g. `lists`, `tags`, `workflows`, `automations`, `channels`, `numbers`, `hooks`. |
| `refresh` | string | no | Set `true` to re-fetch live resources from the provider before listing. Defaults to the cached copy stored at connect time. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-integration-resources", "integrationID": "<your-crm-integration-id>", "resourceType": "lists" }
```

### describe-integration-settings

Describe the settings an integration accepts inside the autoresponder node (the block that runs when someone registers). Returns each field with its value type, whether it is part of the minimum an integration needs, which toggle has to be on for it to apply, and whether configuring it makes registration a metered action. Read it before writing autoresponder integration settings with `change-setting`, because a value of the wrong type or a field whose toggle is off is saved but never used.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `integrationID` | string | no | A connected integration to describe (from `list-campaign-integrations`); its type is resolved for you. |
| `integrationType` | string | no | Describe a type rather than a connection, e.g. `CONVERTKIT`, `INFUSIONSOFT`, `HUBSPOT`. |
| `email` / `tenant_id` | string | one of | Account target. |

Pass an `integrationID` to describe one connected integration, or `integrationType` for any supported type; with neither it returns every type.

```json
{ "tool": "describe-integration-settings", "integrationType": "CONVERTKIT" }
```

### attach-e-s-p-config

Attach an ESP/CRM integration to a campaign by setting its target list and custom-field mapping on the autoresponder. Use `list-integration-resources` to find valid list IDs and custom-field IDs first. Constant Contact has its own tool (`attach-constant-contact-config`).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `integrationID` | string | yes | The connected ESP/CRM integration ID to attach (e.g. an ActiveCampaign or HubSpot integration). |
| `list` | string | conditional | The target list ID on the integration. Required for list-based ESPs (ActiveCampaign, AWeber, CampaignMonitor, GetResponse, Mailchimp). From `list-integration-resources`. |
| `customFields` | object | no | Map of the integration's custom-field ID to a `{value, format}` pair. `value` MUST be a real AEvent merge token (the schema lists `webinar-time`, `replay-time`, `url`, `join` as examples); `format` is a timezone code (`EST`, `PST`) for time values, a keyword (`default`, `startsIn`, `expiresIn`), or a PHP date-format string (`Y-m-d H:i:s`). Optional per-entry keys: `event` (target sub-event ID) and `fallback` (default text). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "attach-e-s-p-config", "wtl": "<your-campaign-id>",
  "integrationID": "<your-crm-integration-id>", "list": "<your-list-id>",
  "customFields": { "<custom-field-id>": { "value": "webinar-time", "format": "EST" } } }
```

> Operational guidance (not schema text): `value` must be a real AEvent merge token — confirm the exact token against your own CRM record after mapping, since only the destination CRM contact card proves the field actually populated. In particular, AVOID `value:"join"` for the join link: `"join"` resolves to the join-TIME field, which is empty before the event, NOT the join URL. Use the canonical join-link token form instead. The four schema examples above are not an exhaustive token list.

### attach-constant-contact-config

Attach a Constant Contact integration to a campaign: set the target list UUID and, optionally, a tag ID to apply to every registrant. Use `list-integration-resources` to find valid list and tag IDs first.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `integrationID` | string | yes | The connected Constant Contact integration ID. |
| `list` | string | yes | The Constant Contact list UUID to add contacts to. From `list-integration-resources`. |
| `tag` | string | no | A Constant Contact tag ID to apply to every registrant. From `list-integration-resources`. Leave empty to attach the list without tagging. |
| `signupForm` | string | no | A Constant Contact signup-form UUID. Recorded for a future signup-form feature; the autoresponder does not apply it yet, so supply it only if pre-seeding that data. |
| `customFields` | object | no | Map of the integration's custom-field ID to a `{value, format}` pair. `value` is an AEvent merge token (e.g. `webinar-time`); `format` is a timezone code (`EST`), a keyword (`default`, `startsIn`), or a PHP date-format string. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "attach-constant-contact-config", "wtl": "<your-campaign-id>",
  "integrationID": "<your-constant-contact-integration-id>", "list": "<your-list-uuid>",
  "tag": "<your-tag-id>" }
```

### audit-campaign-integration-liveness

Cross-check every integration a campaign references (its connected-integrations list, its autoresponder integration map, and its timeline actions) against the live integration catalog. Flags stale integrations (present but disconnected) and orphaned integrations (referenced but missing), which otherwise fail silently.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "audit-campaign-integration-liveness", "wtl": "<your-campaign-id>" }
```

### repair-stale-integration-reference

Re-point a stale or orphaned integration ID to a live integration across a campaign's timeline actions and autoresponder blocks. Use after reconnecting an integration that received a new ID, since reconnecting does not fix existing references. Runs as a preview by default (`dryRun=true`, nothing saved); pass `dryRun=false` to persist.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `fromIntegrationID` | string | yes | The stale or orphaned integration ID currently referenced by the campaign that should be replaced. |
| `toIntegrationID` | string | one of | The live integration ID to re-point references to. Provide this or `toIntegrationName`. |
| `toIntegrationName` | string | one of | The name of the live integration to re-point references to (resolved via `list-integrations`). Provide this or `toIntegrationID`. |
| `dryRun` | boolean | no | Default `true` (report what would change without saving). Pass `false` to persist. |
| `email` / `tenant_id` | string | one of | Account target. |

Example (preview):

```json
{ "tool": "repair-stale-integration-reference", "wtl": "<your-campaign-id>",
  "fromIntegrationID": "<stale-integration-id>", "toIntegrationName": "My ActiveCampaign", "dryRun": true }
```

### add-custom-field

Add a custom field to an integration. Creates a custom field IN a connected integration/CRM so AEvent can map/push a value to it. (Distinct from a registration form field, which `add-field` creates on the AEvent reg form.)

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `integrationID` | string | yes | The integration to add the custom field to. |
| `name` | string | yes | The custom field name. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-custom-field", "wtl": "<your-campaign-id>",
  "integrationID": "<your-crm-integration-id>", "name": "Webinar Source" }
```

### add-system-fields

Add all system custom fields (e.g. `joinURL`) to an integration. Bulk-adds AEvent's system fields into the integration's field set, making AEvent system values available for mapping in the CRM.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `integrationID` | string | yes | The integration to add the system fields to. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-system-fields", "wtl": "<your-campaign-id>",
  "integrationID": "<your-crm-integration-id>" }
```

---

## 4. Registration and Fields

### add-field

Add a single registration field to the campaign's form. `add-field {wtl, name}` creates a REGISTRATION field as a hidden data field: a `customForms` entry of `type:"query"` — collected by the registration endpoint and addressable as `{{!subscriber-<name>!}}`, but NOT a visible input on the form. To add a VISIBLE field to the registration form, use `edit-form` (see "Adding a custom registration question" below). Note this is a registration form field, DIFFERENT from an integration custom field (`add-custom-field`, which targets a CRM/integration).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `name` | string | yes | The registration field name (also the merge bind key, `{{!subscriber-<name>!}}`). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-field", "wtl": "<your-campaign-id>", "name": "Company" }
```

> ## Adding a custom registration question
>
> A registration form has two distinct layers:
>
> - **Data-collection fields** — entries in `registration.customForms[]`, shape `{name, type}`. Per AEvent dev, BOTH `type:"query"` and `type:"custom_form"` only classify data collection; NEITHER renders a visible input. `add-field {wtl, name}` creates one (`type:"query"`); `change-setting op:add path:/customForms/- value:{name, type:"query"}` can also add or edit them. They are collected by the registration endpoint and addressable as `{{!subscriber-<name>!}}` (e.g. `{{!subscriber-firstName!}}`, `{{!subscriber-lastName!}}`).
> - **The visible form** (the inputs the registrant actually sees and fills) — managed with the form-builder tools `edit-form` (write) and `view-form` (read). To add a visible custom question or dropdown the registrant answers, use `edit-form` with a JSON Patch. Call `view-form` first to read the current form, then append a form item:
>
>   ```json
>   { "tool": "edit-form", "wtl": "<your-campaign-id>",
>     "op": "add", "path": "/formItems/-",
>     "value": { "name": "revenue", "label": "Monthly revenue", "type": "text", "required": true } }
>   ```
>
>   Call `view-form` again afterward to confirm the input landed. (Form-builder tools apply only to form-builder campaigns; default-form campaigns return "No form settings".)
>
> So: to add a question the registrant SEES, use `edit-form`. The `customForms` side (`add-field` / `change-setting`) defines the collected, merge-addressable data fields; the `customForms` `type` does NOT make a field visible. Registration fields are also different from integration custom fields (`add-custom-field`, which targets a CRM/integration).

### add-u-t-m

Add the standard UTM parameter set to the registration fields. The live tool name is `add-u-t-m`, displayed "Add U T M".

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-u-t-m", "wtl": "<your-campaign-id>" }
```

---

## 5. Messaging and Automation (timeline builders)

These tools add actions to the campaign timeline. Each shares the `audience` / `eventID` / `integrationID` / `time` quartet defined above. Use `show-merge-fields` first if the copy needs personalization.

### add-email

Add an email action (HTML body).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `subject` | string | yes | Email subject. |
| `message` | string | yes | Email body as HTML. |
| `audience` / `eventID` / `integrationID` / `time` | string | no | Action quartet. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-email", "wtl": "<your-campaign-id>",
  "subject": "You're in, {{!subscriber-firstName!}}",
  "message": "<p>Your join link: {{!subscriber-joinURL!}}</p>",
  "audience": "Registrants", "integrationID": "<your-email-integration-id>",
  "time": "1 day before at 9:00am" }
```

Example stored shape for an email action (`smtp-sendMail`), for reference:

```json
{
  "action": "smtp-sendMail", "actionTitle": "Send Email",
  "groupID": ["3"], "groupName": "Registrants",
  "integrationID": "sg0email0integ01", "integrationName": "My SMTP", "integrationType": "SMTP",
  "isAfter": "1", "isExact": true, "hours": 8, "minutes": 0,
  "model": {
    "nameFrom": "Your Brand", "emailFrom": "hello@example.com",
    "emailSubject": "Today. Here's what you'll walk away with.",
    "editorData": "<p>{{!subscriber-firstName!}}, it's today... {{!subscriber-joinURL!}}</p>"
  }
}
```

### add-text-message

Add a text message (SMS / MMS) action.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `message` | string | yes | The message text. |
| `fileID` | string | no | For MMS: a stored image/video file ID (from `list-images` / `list-videos`). |
| `niftyImage` | string | no | For an MMS with a NiftyImage: pass the file NAME (from `list-nifty-images`) instead of `fileID`. |
| `niftyFields` | array | no | Array of strings for the NiftyImage dynamic fields. |
| `audience` / `eventID` / `integrationID` / `time` | string | no | Action quartet. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-text-message", "wtl": "<your-campaign-id>",
  "message": "{{!subscriber-firstName!}}, we go live in 15 min: {{!subscriber-joinURL!}}",
  "audience": "Registrants", "integrationID": "<your-sms-integration-id>",
  "time": "15 minutes before" }
```

Real stored shape (`twilio-textuser`): `model.phonenumber` may be a literal E.164 number OR the keyword `"service"` (use the account's service number).

### add-autochat

Add an autochat action (simulated live chat message posted in the webinar room).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `message` | string | yes | The chat message. |
| `audience` / `eventID` / `time` | string | no | Action params (no `integrationID`; autochat uses the internal ASTREAM `adminwebinar` engine). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-autochat", "wtl": "<your-campaign-id>",
  "message": "Quick note from the team: the timeline walkthrough is next.",
  "audience": "Registrants", "time": "5 minutes after" }
```

### add-c-t-a-popup

Add an in-player call-to-action popup to the campaign timeline. Shows a headline and a button linking to a URL during the live/replay experience.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `headline` | string | yes | The CTA popup headline. |
| `buttonText` | string | yes | The CTA button label, e.g. `Book a Call Now`. |
| `url` | string | yes | The URL the CTA button links to. |
| `subheadline` | string | no | Smaller text under the headline. |
| `image` | string | no | Image URL to show in the popup. |
| `type` | string | no | Popup style: `sidebar` (default), `countdown`, or `presenter`. |
| `duration` | string | no | How long the popup stays on screen after it appears, e.g. `5 minutes` or `1 hour`. Leave blank to keep it up for the rest of the event. |
| `audience` / `eventID` / `time` | string | no | Action params (no `integrationID`; the popup is an in-player element). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-c-t-a-popup", "wtl": "<your-campaign-id>",
  "headline": "Ready to get started?", "buttonText": "Book a Call Now",
  "url": "https://go.aevent.com/book", "type": "sidebar",
  "audience": "Attendees", "time": "30 minutes after", "duration": "10 minutes" }
```

Wiring note (verified-by-grep, `app/Actions/AddCTAPopup.php`): the CTA popup is stored as a `webinar-cta` native timeline action (the same class of in-player action as `webinar-livechat`/autochat), with `integrationID: "adminwebinar"`. It is a timeline action, not a separate popup block. The stored `model` carries `headline`, `subheadline`, `buttonText`, `url`, `type`, `image`, `style`, `countdown` (`"yes"` when `type` is `countdown`), and the duration expands to `isEnd`/`endHours`/`endMinutes`. The `type` enum (`sidebar`/`countdown`/`presenter`) is server-validated; an out-of-enum `type` returns HTTP 422.

### add-messenger-message

Add a ManyChat Facebook Messenger message action to the campaign. Provide a `fileID` to send media (image/gif). Use `show-merge-fields` for personalization variables.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `message` | string | yes | The Messenger message text. |
| `fileID` | string | no | For a media message (image/gif), the file ID. Leave blank for text-only. |
| `buttonText` | string | no | Call-to-action button label. |
| `buttonURL` | string | no | Call-to-action button URL. Required if `buttonText` is set. |
| `audience` / `eventID` / `integrationID` / `time` | string | no | Action quartet (`integrationID` = the connected ManyChat integration; blank = auto-match). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-messenger-message", "wtl": "<your-campaign-id>",
  "message": "{{!subscriber-firstName!}}, we go live soon: {{!subscriber-joinURL!}}",
  "audience": "Registrants", "integrationID": "<your-manychat-integration-id>",
  "time": "15 minutes before" }
```

Wiring note (verified-by-grep, `app/Actions/AddMessengerMessage.php`): `fileID` is resolved via `UserFile::loadFromCacheBy($fileID)` — the same user-file library that `list-images` (and `list-videos`) return, so source a media `fileID` from `list-images` for an image/gif, exactly like `add-text-message`. The stored action type is `manychat-sendMediaMsg` when a `fileID` is present, else `manychat-sendText`; the media model stores `type:"image"` and the file's `uniqueID`.

### add-webhook-action

Add a webhook action to the campaign timeline. Fires a connected Webhook / Make / Zapier / Pabbly integration at the chosen time.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `slug` | string | no | Path to append to the webhook URL (e.g. an event name). |
| `isPerRegistrant` | boolean | no | If `true`, fire once per registrant with that registrant's data. If `false` (default), fire once with all registrants batched. |
| `audience` / `eventID` / `integrationID` / `time` | string | no | Action quartet (`integrationID` = the connected Webhook/Make/Zapier/Pabbly integration; blank = auto-match). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-webhook-action", "wtl": "<your-campaign-id>",
  "integrationID": "<your-webhook-integration-id>", "isPerRegistrant": true,
  "audience": "Registrants", "time": "5 minutes after", "slug": "webinar-registered" }
```

Operational guidance (not schema text): the `slug` is a PATH suffix, not a query string. A `?query` placed in the slug is URL-encoded to a `%3F` path segment and the webhook 404s. Keep the slug to a bare path token (e.g. `webinar-registered`), never `path?key=value`.

### add-voice-mail

Add a ringless voicemail action. Get the audio `fileID` from `list-audio` first.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `fileID` | string | yes | Audio file ID (from `list-audio`). |
| `audience` / `eventID` / `integrationID` / `time` | string | no | Action quartet (use a ringless-voicemail integration, e.g. SLYBROADCAST). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-voice-mail", "wtl": "<your-campaign-id>",
  "fileID": "<audio-file-id-from-list-audio>",
  "audience": "Non-Attendees", "integrationID": "<your-voicemail-integration-id>", "time": "1 hour before" }
```

### add-join-message

Add an on-join welcome message shown in chat after a registrant joins.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `message` | string | yes | Message shown in chat to the viewer. |
| `name` | string | yes | Name of the registrant "sending" the message. |
| `after` | integer | no | Seconds after join before the message is shown. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-join-message", "wtl": "<your-campaign-id>",
  "message": "Welcome! Drop your #1 question in chat.", "name": "Support Team", "after": 30 }
```

### clear-join-messages

Delete ALL on-join welcome messages for the campaign. Destructive; deletes every join message.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "clear-join-messages", "wtl": "<your-campaign-id>" }
```

---

## 5b. Timeline maintenance (delete / retry)

### bulk-delete-action

Remove every action of one type (e.g. `webinar-livechat` for autochats, `twilio-textuser` for text messages) from the campaign timeline. Extremely destructive: it deletes every action of that type at once.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `actionType` | string | yes | The action type to remove, e.g. `webinar-livechat`, `twilio-textuser`. Obtain it from `show-campaign` (`section: "actions"`). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "bulk-delete-action", "wtl": "<your-campaign-id>", "actionType": "twilio-textuser" }
```

### retry-action

Re-run a timeline action that already passed. Recreates the schedule entry for that action and executes its job right away, so the action fires again for everyone it applies to.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `actionID` | string | yes | The ID of the timeline action to re-run, e.g. `_3todnxkr1`. |
| `webinar` | string | no | The `uniqueID` of the webinar, or its `webinarID`. Prefer the `uniqueID`: a `webinarID` is shared by every occurrence of a delegated event. |
| `job` | string | no | The job class that runs the action, e.g. `HubspotJob`. Resolved from the action's integration when omitted. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "retry-action", "actionID": "_3todnxkr1", "webinar": "<your-webinar-uniqueID>" }
```

---

## 6. Tags and Lists (CRM sync actions)

### add-tag

Add an "Add Tag" action to the timeline.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `tag` | string | yes | Tag name to send. |
| `audience` / `eventID` / `integrationID` / `time` | string | no | Action quartet (use the CRM integration that owns the tag). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-tag", "wtl": "<your-campaign-id>",
  "tag": "aevent-attended", "audience": "Attendees",
  "integrationID": "<your-crm-integration-id>", "time": "45 minutes after" }
```

Real stored shape (`gohighlevel-addTag`): note `groupID` can be a UUID for a custom behavioral segment (e.g. `attended-thru-cta`) rather than a default audience id.

### remove-tag

Add a "Remove Tag" action to the timeline.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `tag` | string | yes | Tag name to remove. |
| `audience` / `eventID` / `integrationID` / `time` | string | no | Action quartet. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "remove-tag", "wtl": "<your-campaign-id>",
  "tag": "webinar-lead", "audience": "Attendees", "integrationID": "<your-crm-integration-id>" }
```

### add-to-list

Add a "list" action (add to list, subscribe to campaign, add to workflow, sequence, or group).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `target` | string | yes | Name of the list / campaign / workflow / sequence / group to target. |
| `audience` / `eventID` / `integrationID` / `time` | string | no | Action quartet. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-to-list", "wtl": "<your-campaign-id>",
  "target": "Attended Webinar", "audience": "Attendees", "integrationID": "<your-crm-integration-id>" }
```

---

## 7. Pages

### set-confirmation-page

Set the confirmation page URL for a campaign.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `url` | string | yes | Confirmation page link. |
| `email` / `tenant_id` | string | one of | Account target. |

### set-expired-page

Set the expired page URL for a campaign.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `url` | string | yes | Expired page link. |
| `email` / `tenant_id` | string | one of | Account target. |

### set-replay-page

Set the replay page URL for a campaign.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `url` | string | yes | Replay page link. |
| `email` / `tenant_id` | string | one of | Account target. |

Example for all three:

```json
{ "tool": "set-confirmation-page", "wtl": "<your-campaign-id>", "url": "https://go.aevent.com/webinar-confirmation" }
{ "tool": "set-expired-page",      "wtl": "<your-campaign-id>", "url": "https://go.aevent.com/expired" }
{ "tool": "set-replay-page",       "wtl": "<your-campaign-id>", "url": "https://go.aevent.com/webinar-replay" }
```

### set-replay-sequence

Set the replay availability window for a campaign.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `enabled` | boolean | yes | Is the replay sequence enabled? |
| `days` | integer | no | Days the replay is available. |
| `hours` | integer | no | Hours available. |
| `minutes` | integer | no | Minutes available. |
| `expiresMidnight` | boolean | no | Does the replay expire at midnight? |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "set-replay-sequence", "wtl": "<your-campaign-id>",
  "enabled": true, "days": 3, "hours": 0, "minutes": 0, "expiresMidnight": true }
```

### check-page

Check whether a web page has any header-script issues (e.g. AEvent pixel/embed problems).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `url` | string | yes | The page to test. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "check-page", "url": "https://go.aevent.com/webinar-confirmation" }
```

---

## 8. Video and Content

### list-videos

List user video files. Returns file details keyed by video fileID.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | no | Video name or partial. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-videos" }
```

Real return (trimmed):

```json
{
  "aaa111vid222ccc": { "name": "MyWebinar.mp4:::", "length": "00:30:07", "mimeType": "video/mp4", "status": "Encoded", "size": "0.08" },
  "ddd333vid444fff": { "name": "Demo_Webinar.mp4", "length": "00:35:00", "mimeType": "video/mp4", "status": "Encoded", "size": "0.23" }
}
```

`status: "Encoded"` means ready to attach. The fileID is what you pass to `switch-video.fileID`. The campaign's `webinarVideoLength` (e.g. 7200 = 2h) derives from the attached video and drives like-live scheduling/overlap math.

### upload-media

Import a media file (such as a webinar video) into the media library from a publicly fetchable HTTP(S) URL. AEvent downloads the file from the URL and transcodes it server-side; the bytes are not sent through this tool. Ingestion runs in the background: poll `list-videos` until the file reads `Encoded`.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `url` | string | yes | A publicly fetchable HTTP(S) URL to the source media file. AEvent downloads and transcodes it server-side; you do not upload the bytes through this tool. |
| `name` | string | yes | The file name to store the media under. Plain filename (letters, numbers, spaces, dashes, parentheses) and MUST include a file extension (e.g. `welcome-webinar.mp4`). No folder paths or slashes. |
| `keepLast` | integer | no | Keep only the last N seconds of the source, to trim leading pre-roll (mirrors the cloud-recording import). Omit to import the entire file. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "upload-media", "url": "https://example.com/webinar.mp4", "name": "welcome-webinar.mp4" }
```

### switch-video

Switch the active video for a campaign (optionally for a single additional event).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `fileID` | string | yes | Desired video ID (from `list-videos`; status must read `Encoded`). |
| `eventID` | string | no | Change the video only for a specific additional event (from `list-multi-events`); omit for the main event. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "switch-video", "wtl": "<your-campaign-id>",
  "fileID": "<your-video-file-id>" }
```

### show-transcript

Show the video transcript for a campaign. Returns timestamped WebVTT.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "show-transcript", "wtl": "<your-campaign-id>" }
```

### get-video-frame

Take a still frame of a video at a point in time and upload it as a user image file. Returns/creates a user image file (usable e.g. as MMS media via its returned fileID). Use case: generate a slide/still from a video.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `fileID` | string | yes | The video file ID (from `list-videos`) to grab the frame from. |
| `timestamp` | integer | yes | Seconds into the video at which to take the frame. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "get-video-frame",
  "fileID": "<your-video-file-id>", "timestamp": 90 }
```

### list-audio

List user audio files (for ringless voicemail). Returns file details keyed by file ID.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | no | Audio name or partial. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-audio" }
```

### list-images

List user image files. Returns file details keyed by file ID. (Use a fileID with `add-text-message.fileID` for MMS.)

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | no | Image name or partial. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-images" }
```

### list-nifty-images

List user NiftyImage files (personalized images for MMS). Returns file details keyed by file NAME (not ID). Pass the name to `add-text-message.niftyImage`.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | no | Image name or partial. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-nifty-images" }
```

---

## 9. Multi-Events

### list-multi-events

List multi-events for a campaign. Returns additional-event name keyed by `eventID`.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-multi-events", "wtl": "<your-campaign-id>" }
```

Real return:

```json
{ "_f5g6h7i8j": "Day1", "_a1b2c3d4e": "Day2", "_k9l0m1n2o": "NonAttendeePost" }
```

These keys are `eventID`s (pass to `add-additional-event`, an action's `eventID`, or `switch-video.eventID`).

### describe-additional-events

Describe the main event and every additional event of a campaign as a table of event ID, name, time, type, and videos.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "describe-additional-events", "wtl": "<your-campaign-id>" }
```

### add-additional-event

Add an additional event to the campaign (fans out from a source event/audience).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `name` | string | yes | The event name. |
| `audience` / `eventID` / `time` | string | no | Action params (`eventID` here selects the source event to fan out from). |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "add-additional-event", "wtl": "<your-campaign-id>",
  "name": "Day3", "audience": "Registrants", "eventID": "_a1b2c3d4e", "time": "1 day before at 11:00am" }
```

Wiring note: in stored config the additional event is an `automa8-addEvent` action under `general.multiEvents`. `integrationType: "AUTOMA8"` ("AEvent Functions") is the internal engine that creates events; it is NOT a customer-connected integration.

### add-many-additional-events

Add several additional events to the campaign at once (up to 30). If a build errors partway (partial or duplicated events), call `describe-additional-events` to list current events, then `delete-additional-events` by eventID to reset to a clean state BEFORE retrying. Never retry on top of a half-built table; that compounds into duplicate IDs / orphans.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `events` | array | yes | The events to add in run order (max 30). Each item is `{name, offset}`. `offset` is seconds after the previous event (min 900 = 15 min); the FIRST offset is measured from the LAST EXISTING event. |
| `email` / `tenant_id` | string | one of | Account target. |

Offsets are BETWEEN consecutive events, not absolute times. Example: three sessions/day at 7:30am / 1:00pm / 10:30pm across two days means `offset` = the gap to the previous session (19800s = 5h30m for 7:30 to 1:00, 34200s = 9h30m for 1:00 to 10:30, 32400s = 9h for 10:30 to next-day 7:30).

Example:

```json
{ "tool": "add-many-additional-events", "wtl": "<your-campaign-id>",
  "events": [
    { "name": "D1 1pm",     "offset": 19800 },
    { "name": "D1 10:30pm", "offset": 34200 },
    { "name": "D2 7:30am",  "offset": 32400 }
  ] }
```

### delete-additional-events

Remove additional events from the campaign. Obtain the event IDs from `describe-additional-events`.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `eventIDs` | array | yes | The IDs of the additional events to remove, e.g. `["_bv4hBQyP8"]`. From `describe-additional-events`. |
| `email` / `tenant_id` | string | one of | Account target. |

Example:

```json
{ "tool": "delete-additional-events", "wtl": "<your-campaign-id>",
  "eventIDs": ["_bv4hBQyP8", "_a1b2c3d4e"] }
```

---

## 9b. Recurring delete-markers

Delete markers are records created when a user deletes an instance of a recurring webinar. The recurring scheduler skips those slots so the deleted instance does not regenerate. These two tools inspect and reset that state.

### list-delete-markers

Lists the delete markers generated when a user deletes an instance of a recurring webinar. The recurring scheduler skips these times.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | no | Campaign ID to filter by. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-delete-markers", "wtl": "<your-campaign-id>" }
```

### clear-delete-markers

Clears the delete markers that prevent the recurring scheduler from creating another webinar for a slot the user deleted. Use it to make the scheduler refill a previously deleted recurring slot.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | no | Campaign ID to filter by. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "clear-delete-markers", "wtl": "<your-campaign-id>" }
```

---

## 10. Audiences

### list-audiences

List audiences for a campaign. Returns audience name keyed by its ID.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "list-audiences", "wtl": "<your-campaign-id>" }
```

Real return:

```json
{ "3": "Registrants", "a1": "Attendee", "a2": "Non-Attendee" }
```

These three defaults exist on every campaign; the `id` is what you pass to an action's `audience`. Campaigns may add richer custom behavioral segments (UUID-keyed, e.g. `attended-thru-cta`); always resolve them per-campaign.

---

## 11. Forms

Most campaigns return an empty-state string because they use the default registration form, not the form builder. The form-builder JSON only exists once a campaign has been edited through the form builder. For default-form campaigns, the registration copy lives in `show-campaign?section=registration` (`registrationSubject` / `registrationBody` / `customForms`).

### view-form

Read a campaign's form-builder settings (HTML preview by default, or raw JSON).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `raw` | boolean | no | `true` returns the raw settings JSON instead of the HTML preview. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "view-form", "wtl": "<your-campaign-id>", "raw": true }
```

Example return (a default-form campaign):

```
No form settings. The user may not be using the form builder for this campaign.
```

This is the literal expected response, NOT an error.

### edit-form

Write a campaign's form-builder settings. Accepts HTML, JSON, or a JSON Patch. Always call `view-form` first, and call it again to check your work. Prefer JSON Patch for safety: it lets you add a single visible input without rewriting the whole form.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `html` | string | one of | The full form HTML. |
| `json` | object | one of | The raw form JSON, for when converting HTML does not work. |
| `op` | string | for JSON Patch | JSON-Patch op (`add`, `remove`, `replace`). |
| `path` | string | for JSON Patch | JSON-Pointer path, e.g. `/formItems/-` to append a visible input. |
| `value` | any | for add/replace | The patch value (e.g. the new form-item object). |
| `email` / `tenant_id` | string | one of | Account target. |

Add a single visible form input via JSON Patch (preferred):

```json
{ "tool": "edit-form", "wtl": "<your-campaign-id>",
  "op": "add", "path": "/formItems/-",
  "value": { "name": "revenue", "label": "Monthly revenue", "type": "text", "required": true } }
```

Or replace the whole form via HTML:

```json
{ "tool": "edit-form", "wtl": "<your-campaign-id>",
  "html": "<form>...registration fields...</form>" }
```

---

## 12. Lead Management

### show-lead

Information about a single registrant: which integrations ran, and which webinar they signed up for.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | yes | The registrant's email address OR registrant ID. This is the param that identifies WHICH registrant to look up. |
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target (standard meaning, same as every other tool). |

```json
{ "tool": "show-lead", "wtl": "<your-campaign-id>",
  "search": "registrant@example.com" }
```

`show-lead` reports the per-registrant integration run-state (which integrations ran for that lead). If it returns incomplete run-state, flag it up front and lower confidence rather than theorizing a root cause from logs/code alone.

---

## 13. Low-level: change-setting (JSON-Patch editor) ★

`change-setting` is the highest-value low-level tool: a single-operation JSON-Patch editor over a campaign's settings nodes. Use it for a field that has no purpose-built tool.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `nodeID` | string | yes | The settings node to modify. Accepts SIX values: **`general`**, **`registration`** (incl. `/customForms`), **`autoresponder`**, **`integration`**, **`page`**, **`events`**. E.g. add a registration DATA field with `op:add path:/customForms/- value:{name, type:"query"}` on the `registration` node (data-collection only; the visible form input is added via `edit-form` — see "Adding a custom registration question"). Under the **`autoresponder`** node, each integration block is validated a SECOND time against that integration type's own rules, so run `describe-integration-settings` first to learn the accepted fields/toggles. For timeline actions, still prefer the `add-*` tools (raw action edits diverge from the live action cache). |
| `op` | string | yes | JSON-Patch op. **Only `add`, `remove`, `replace` work** (verified). `move`/`copy` are unusable (the tool has no `from` parameter and the server rejects them); `test` is rejected as an invalid op. |
| `path` | string | yes | JSON-Pointer path within the node, e.g. `/timelineName`. |
| `value` | any | for add/replace | The patch value. No `type` constraint in the schema: it accepts any JSON type. |
| `email` / `tenant_id` | string | one of | Account target. |

Safe example ops:

Rename the campaign:

```json
{ "tool": "change-setting", "wtl": "<your-campaign-id>",
  "nodeID": "general", "op": "replace", "path": "/timelineName", "value": "My Webinar (renamed)" }
```

Append to an array field (verified pattern: append with `/-`, replace a whole array with `/arrayField`, remove by index with `/arrayField/N`):

```json
{ "tool": "change-setting", "wtl": "<your-campaign-id>",
  "nodeID": "general", "op": "add", "path": "/userGroup/-", "value": { } }
```

Critical caveats:

- `change-setting` validates JSON type/structure AND value-domains/enums. A bad value (e.g. an out-of-domain `webinarType`) is REJECTED, not persisted. The tool returns a clear validation message naming the offending field/constraint, e.g. `The general setting is invalid: [timelineName] Array value found, but a string is required`. Read the message to fix the value.
- It mutates the source-of-truth settings. The action CACHE is updated separately by the `add-*` tools, so editing actions via raw patch can diverge from what the `add-*` tools manage. Prefer the purpose-built `add-*` / `set-*` tools for actions; reserve `change-setting` for `general`- or `registration`-node fields with no dedicated tool.
- **Arrays:** append with `path:"/field/-"`, replace a whole array with `path:"/field"`, remove by index with `path:"/field/N"`. Appending to a field that is not already an array fails. A bare-root `replace` on `path:"/"` also fails.
- **Errors:** failed patches surface a clear validation message (e.g. `The general setting is invalid: [timelineName] Array value found, but a string is required`) that names the offending field/constraint; re-check the `path`, the value type, and which node you are writing. Unsupported ops fail distinctly with "The selected op is invalid."

---

## 14. Billing

### billing-details

Billing and plan information for the account. Account-level (no `wtl`).

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "billing-details" }
```

---

## 15. Account (programmatic registration)

### show-secret

Show the account's own api-registration secret used for programmatic registration. Returns a BARE ~15-character string (no field wrapper). The secret is combined with `wtl` + `email` against the AEvent `/api-registration` endpoint to register a lead programmatically. Account-level (no `wtl`); takes no parameters.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| (none) | — | — | Takes no parameters; scoped to the connected account. |

```json
{ "tool": "show-secret" }
```

---

## 16. Diagnostics

### search-logs

Search the background job logs to troubleshoot user issues. Logs can take up to an hour to appear, so prefer tools like `show-lead` when possible.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | yes | Search terms, separated by commas. Each term filters the results further. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "search-logs", "search": "webinar,123456" }
```

### verify-build-complete

Run a read-only pre-launch check on a campaign: empty message bodies, literal placeholder copy, and unfinished action skeletons. Empty GoToWebinar autoresponder slots are only flagged on GoToWebinar delivery when the slot is enabled; filled slots on other platforms are reported as inert copy.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `wtl` | string | yes | Campaign ID. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "verify-build-complete", "wtl": "<your-campaign-id>" }
```

---

## 17. Docs

### search-documentation

Search the API documentation. Returns routes whose path contains the search string.

| Param | Type | Required | Notes |
|-------|------|----------|-------|
| `search` | string | yes | Only routes with this string in the path are returned. |
| `email` / `tenant_id` | string | one of | Account target. |

```json
{ "tool": "search-documentation", "search": "webinar" }
```

Note: `search-documentation` is present on the consumer proxy surface used for these tests. The public AEvent MCP server may expose additional discovery tools; if a `search-documentation` (or similar) appears on the public server but not the proxy, treat that as an expected surface difference and flag it.

---

# MERGE FIELDS

**THE RULE: the token NAME inside the braces must EXACTLY match a name returned by `show-merge-fields`.** AEvent treats the brace/bang variants as equivalent — `{{!var!}}`, `{{!var}}`, `{!var}`, `{var}`, `{{var}}`, `%7Bvar%7D` all resolve the same — so the brace style is not the rule; the name is. The examples below are shown in the form `show-merge-fields` returns them (e.g. `{{!subscriber-firstName!}}`). The authoritative list comes from `show-merge-fields` (account-level, no `wtl`); always copy the exact name it returns.

```json
{ "tool": "show-merge-fields" }
```

## Most-used tokens (verified in real campaign copy)

| Token | Meaning |
|-------|---------|
| `{{!subscriber-firstName!}}` | First name |
| `{{!subscriber-lastName!}}` | Last name |
| `{{!subscriber-email!}}` | Email |
| `{{!subscriber-phone!}}` | Phone number |
| `{{!subscriber-joinURL!}}` | Join link |
| `{{!subscriber-replayURL!}}` | Replay link |
| `{{!replay-replayURL!}}` | Replay link (alternate; both resolve to the replay link) |
| `{{!subscriber-registrationDate!}}` | Registration date |
| `{{!subscriber-uuid!}}` | Registrant UID |

## Webinar time tokens (per timezone)

Account-timezone vs registrant-timezone variants exist for most time fields (registrant variants insert `-reg-`).

| Token | Meaning |
|-------|---------|
| `{{!webinar-esttime!}}` | Start in America/New_York (EST) |
| `{{!webinar-psttime!}}` | Start in America/Los_Angeles (PST) |
| `{{!webinar-csttime!}}` | Start in America/Chicago (CST) |
| `{{!webinar-msttime!}}` | Start in America/Denver (MST) |
| `{{!webinar-usertime!}}` | Start in the account timezone |
| `{{!webinar-gmttime!}}` / `{{!webinar-bsttime!}}` / `{{!webinar-cettime!}}` / `{{!webinar-aesttime!}}` / `{{!webinar-jsttime!}}` / `{{!webinar-isttime!}}` / `{{!webinar-msktime!}}` | Start in GMT / London / Paris / Sydney / Tokyo / Kolkata / Moscow |
| `{{!webinar-dayofweek!}}` | Day of week (account tz) |
| `{{!webinar-day!}}` / `{{!webinar-month!}}` / `{{!webinar-year!}}` | Date parts (account tz) |
| `{{!webinar-reg-dayofweek!}}` / `{{!webinar-reg-day!}}` / `{{!webinar-reg-month!}}` / `{{!webinar-reg-year!}}` | Date parts in the registrant's timezone |
| `{{!webinar-webinarTitle!}}` / `{{!webinar-webinarBody!}}` | Registration title / description (topic / agenda if Zoom) |
| `{{!webinar-startsIn!}}` | Seconds until start |

Replay-expiration tokens mirror the webinar set under the `{{!replay-...!}}` prefix (e.g. `{{!replay-esttime!}}`, `{{!replay-day!}}`, `{{!replay-expiresIn!}}`, `{{!replay-timeStamp!}}`).

## Calendar tokens

```
{{!add_to_google_calendar!}}      Link to add to Google Calendar
{{!add_to_apple_calendar!}}       Link to add to other calendar types
```

Match the name (`add_to_google_calendar`, `add_to_apple_calendar`) exactly as `show-merge-fields` lists it; the brace/bang variants resolve the same.

> ## Common pitfalls callout: wrong token names do not work
>
> These tokens are ALL WRONG and will render literally instead of personalizing — NOT because of the braces, but because the NAME does not exist in `show-merge-fields`. `{{first_name}}` fails because there is no `first_name` token; the name is `subscriber-firstName`.
>
> | WRONG (no such name) | CORRECT (name from show-merge-fields) |
> |-------|---------|
> | `{{first_name}}` | `{{!subscriber-firstName!}}` |
> | `{{webinar_link}}` | `{{!subscriber-joinURL!}}` |
> | `{{replay_link}}` | `{{!subscriber-replayURL!}}` (or `{{!replay-replayURL!}}`) |
> | `{{webinar_date}}` | `{{!webinar-day!}}` / `{{!webinar-month!}}` / `{{!webinar-year!}}` or a `{{!webinar-...time!}}` token |
>
> Rule: match the token name exactly to what `show-merge-fields` returns. The brace/bang style does not matter; the name does. Always confirm with `show-merge-fields` before drafting AEvent-native copy.

---

# TOOL SUMMARY TABLE

| Tool | Category | Read/Write | Key params (server-required) |
|------|----------|-----------|------------------------------|
| `list-campaigns` | Campaign Mgmt | read | (search) |
| `create-campaign` | Campaign Mgmt | write | name, integrationID |
| `show-campaign` | Campaign Mgmt | read | wtl, (section) |
| `schedule-webinar` | Webinar Sched | write | wtl, integrationID, timestamp |
| `edit-webinar` | Webinar Sched | write | id, timestamp |
| `edit-recurring` | Webinar Sched | write | recurringID, recurring |
| `list-upcoming-webinars` | Webinar Sched | read | (wtl, page) |
| `list-past-webinars` | Webinar Sched | read | (wtl, page) |
| `list-recurring` | Webinar Sched | read | (wtl) |
| `list-schedules` | Webinar Sched | read | (webinarID, actionType, ...) |
| `pre-schedule-guard` | Webinar Sched | read | wtl, (timestamp) |
| `show-event-info` | Webinar Sched | read | id (instance id) |
| `list-integrations` | Integrations | read | (search) |
| `list-campaign-integrations` | Integrations | read | wtl |
| `connect-integration` | Integrations | write | type, credentials |
| `check-integration-credential` | Integrations | read | type, credentials |
| `test-integration` | Integrations | read | integrationID |
| `list-integration-resources` | Integrations | read | integrationID |
| `describe-integration-settings` | Integrations | read | (integrationID or integrationType) |
| `attach-e-s-p-config` | Integrations | write | wtl, integrationID, (list) |
| `attach-constant-contact-config` | Integrations | write | wtl, integrationID, list |
| `audit-campaign-integration-liveness` | Integrations | read | wtl |
| `repair-stale-integration-reference` | Integrations | write | wtl, fromIntegrationID, (toIntegrationID or toIntegrationName) |
| `add-custom-field` | Integrations | write | wtl, integrationID, name |
| `add-system-fields` | Integrations | write | wtl, integrationID |
| `add-field` | Registration | write | wtl, name |
| `add-u-t-m` | Registration | write | wtl |
| `add-email` | Messaging | write | wtl, subject, message |
| `add-text-message` | Messaging | write | wtl, message |
| `add-autochat` | Messaging | write | wtl, message |
| `add-c-t-a-popup` | Messaging | write | wtl, headline, buttonText, url |
| `add-messenger-message` | Messaging | write | wtl, message |
| `add-webhook-action` | Messaging | write | wtl |
| `add-voice-mail` | Messaging | write | wtl, fileID |
| `add-join-message` | Messaging | write | wtl, message, name |
| `clear-join-messages` | Messaging | write | wtl |
| `bulk-delete-action` | Timeline maint | write | wtl, actionType |
| `retry-action` | Timeline maint | write | actionID |
| `add-tag` | Tags & Lists | write | wtl, tag |
| `remove-tag` | Tags & Lists | write | wtl, tag |
| `add-to-list` | Tags & Lists | write | wtl, target |
| `set-confirmation-page` | Pages | write | wtl, url |
| `set-expired-page` | Pages | write | wtl, url |
| `set-replay-page` | Pages | write | wtl, url |
| `set-replay-sequence` | Pages | write | wtl, enabled |
| `check-page` | Pages | read | url |
| `list-videos` | Video & Content | read | (search) |
| `upload-media` | Video & Content | write | url, name |
| `switch-video` | Video & Content | write | wtl, fileID |
| `get-video-frame` | Video & Content | write | fileID, timestamp |
| `show-transcript` | Video & Content | read | wtl |
| `list-audio` | Video & Content | read | (search) |
| `list-images` | Video & Content | read | (search) |
| `list-nifty-images` | Video & Content | read | (search) |
| `list-multi-events` | Multi-Events | read | wtl |
| `describe-additional-events` | Multi-Events | read | wtl |
| `add-additional-event` | Multi-Events | write | wtl, name |
| `add-many-additional-events` | Multi-Events | write | wtl, events |
| `delete-additional-events` | Multi-Events | write | wtl, eventIDs |
| `list-delete-markers` | Recurring markers | read | (wtl) |
| `clear-delete-markers` | Recurring markers | write | (wtl) |
| `list-audiences` | Audiences | read | wtl |
| `view-form` | Forms | read | wtl, (raw) |
| `edit-form` | Forms | write | wtl, (html OR json OR op+path+value) |
| `show-lead` | Lead Mgmt | read | search (REGISTRANT), wtl |
| `change-setting` | Low-level | write | wtl, nodeID, op, path, (value) |
| `billing-details` | Billing | read | (account) |
| `show-secret` | Account | read | (account) |
| `search-logs` | Diagnostics | read | search |
| `verify-build-complete` | Diagnostics | read | wtl |
| `search-documentation` | Docs | read | search |
| `show-merge-fields` | Merge Fields | read | (account) |

Total: 72 consumer tools.

---

# APPENDIX: in-schema quirks and gotchas

- **No `required` arrays.** Every parameter is schema-optional; requirements are server-enforced. This doc states the functional requirements.
- **`show-lead` uses `search` for the registrant.** Look a registrant up with `search` (their email OR registrant ID). `email` / `tenant_id` remain the account target, exactly like every other tool — there is no `email`-means-registrant overload.
- **`change-setting` `nodeID` accepts six nodes.** `general`, `registration` (incl. `/customForms`), `autoresponder`, `integration`, `page`, `events`. Under `autoresponder`, each integration block is re-validated against its type's own rules — run `describe-integration-settings` first.
- **`change-setting` validates types AND enums/value-domains.** A bad value (wrong `webinarType`, etc.) is rejected, not silently saved. It returns a clear validation message naming the offending field/constraint (e.g. `The general setting is invalid: [timelineName] Array value found, but a string is required`).
- **`schedule-webinar` / `edit-recurring` share one 13-field `recurring` spec.** `isRecurring | startTime | endTime | lengthMINUTES | period | interval | weekdays | startDate | expiration | mode | encoreTimeline | encoreTimestamp | timezone`. `period` includes `MINUTE`; `mode` is one of `WEB, WEBINAR, MEETING, CLASSIC, BROADCAST, OBVIO, YOUTUBE`; `timezone` = an IANA name or `REGISTRANT` for evergreen (registrant-tz or page-load-`RELATIVE`).
- **`add-webhook-action.slug` is a path suffix (operational).** A `?query` in the slug encodes to a `%3F` path segment and 404s; keep the slug a bare path token. (Operational guidance, not schema text.)
- **`attach-e-s-p-config.customFields.value` must be a real merge token (operational).** Avoid `value:"join"` for the join link — it resolves to the empty join-TIME, not the join URL. Confirm the mapped value in the destination CRM contact card. (Operational guidance, not schema text.)
- **`add-c-t-a-popup` / `add-messenger-message` wiring (verified-by-grep).** The CTA popup stores as a `webinar-cta` timeline action (`integrationID: "adminwebinar"`); the Messenger action stores as `manychat-sendMediaMsg`/`manychat-sendText` and its media `fileID` sources from `list-images`.
- **Description-only enums.** `create-campaign.options` and `show-campaign.section` are not schema-enforced; supply correct values.
- **Empty arrays are normal.** `list-recurring` / `list-upcoming-webinars` / `list-past-webinars` / `list-schedules` return `[]` when nothing matches; this is not an error.
- **`connected: true` and `test-integration` are shallow.** Neither validates the OAuth token or plan depth. Use `audit-campaign-integration-liveness` for a deeper cross-check of a campaign's integration references.
- **Multiple records per integration type.** Resolve the exact `integrationID` per campaign via `list-campaign-integrations`, never by type alone.

---

*Source of truth: the live AEvent MCP tool schemas. Write tools shown as call shapes only. All example IDs and names are generic placeholders.*
