---
title: "Build an agenda and connect speakers"
description: "Turn event notes into a reviewed agenda, create speakers and sessions, and connect them through the Core API."
---

You can use the Core API to create speakers and content sessions, then connect them. An AI agent can help structure messy notes and prepare the requests. It needs an authorized tool capable of making HTTPS requests to execute them; the [Brella agent skill](https://docs.brella.io/agents) supplies the workflow and API knowledge.

## Review the source material

Ask the agent to extract a table with session title, date, start time, duration, track, location, description and speakers. Keep missing values marked as unresolved. Confirm the event's timezone and dates, duplicate speaker names, and whether each item should create or update a record.

Use a stable external ID for each session and speaker. Keep a mapping from that source ID to the returned Brella ID. A matching name alone is not a reliable identity; ask about ambiguous matches.

## Prepare permissions

Your Core API token needs access to the event and these resource scopes:

| Resource | Actions and fields for this recipe |
| --- | --- |
| Events | Read the event's name and dates. Obtain the timezone from the organizer; this Core resource does not expose a timezone field. |
| Tracks | Read existing tracks; enable create with `name` if you need a new content track. |
| Speakers | Read/create; allow `external_id`, `first_name`, `last_name` and any optional profile inputs. Enable the `external_id` query filter for reconciliation. |
| Content sessions | Read/create/update; allow `external_id`, `title`, `track_id`, `start_time`, `duration`, `speaker_assignments`, and `content` or `location` if used. Enable the `external_id` query filter and the `speaker_assignments` read association. |

Select response fields you need for verification. Write-response fields are separate from readable fields. You can always save the returned `data.id`.

## Create or reuse the speakers

Read existing speakers, using an enabled `external_id` filter when available. Create only those that are missing. The speaker resource requires `first_name`; optional biography text is accepted by `bio` and normalized by Brella.


Example request with fictional IDs. Set BRELLA_API_BASE_URL and BRELLA_API_ACCESS_TOKEN in the execution environment. Optional inputs are documented in the resource input reference.

```shell
curl --fail-with-body --silent --show-error --globoff --max-time 30 --request POST \
  --url "${BRELLA_API_BASE_URL}"'/api/core/organizations/123/events/456/speakers' \
  --header "Accept: application/vnd.brella.v4+json" \
  --header "Brella-API-Access-Token: ${BRELLA_API_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data-raw '{
  "external_id": "SPEAKER-42",
  "first_name": "Taylor",
  "last_name": "Ng"
}'
```



Save the actual IDs from the **201 Created** responses. The numbers in the examples are fictional. Core does not expose a speaker DELETE operation, so do not plan an automatic rollback that depends on one.

## Resolve the content track

Read `/tracks` for this event and select a content or external-content track. Networking tracks cannot hold content sessions. If necessary, create a track with `name`; the Core track create operation creates a content track.


Example request with fictional IDs. Set BRELLA_API_BASE_URL and BRELLA_API_ACCESS_TOKEN in the execution environment. 

```shell
curl --fail-with-body --silent --show-error --globoff --max-time 30 --request POST \
  --url "${BRELLA_API_BASE_URL}"'/api/core/organizations/123/events/456/tracks' \
  --header "Accept: application/vnd.brella.v4+json" \
  --header "Brella-API-Access-Token: ${BRELLA_API_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data-raw '{
  "name": "Main stage"
}'
```



Using an existing, verified `track_id` makes the import easier to review. The session `track_name` input can also find or create a content track when no track ID is supplied.

## Create one session first

Provide an ISO 8601 start time with an explicit timezone offset and a duration in minutes. Convert the agreed event-local time to that instant deliberately. Brella's timeslot flow applies event timezone compatibility logic; verify the displayed time in the organizer agenda instead of assuming a returned timestamp can be echoed back unchanged.

The session and its end time must fit within the event dates. Use `duration`; although `end_time` is exposed in the wrapper, the current write parameter allowlist discards it.


Example request with fictional IDs. Set BRELLA_API_BASE_URL and BRELLA_API_ACCESS_TOKEN in the execution environment. Optional inputs are documented in the resource input reference.

```shell
curl --fail-with-body --silent --show-error --globoff --max-time 30 --request POST \
  --url "${BRELLA_API_BASE_URL}"'/api/core/organizations/123/events/456/content-sessions' \
  --header "Accept: application/vnd.brella.v4+json" \
  --header "Brella-API-Access-Token: ${BRELLA_API_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data-raw '{
  "title": "Opening keynote",
  "track_id": 501,
  "start_time": "2026-09-03T09:00:00Z",
  "duration": 60
}'
```



Use **Add optional fields** to include `external_id`, `location` and `speaker_assignments`. Each assignment contains a real `speaker_id` from this event and an optional `role`, such as `Speaker` or `Moderator`. Array order determines display position.

For a session description, use the builder's **Write a session description** control. It converts plain text to the DraftJS JSON object required by `content`. A plain string or HTML string is rejected for this field. Speaker `bio` has different rules and accepts text.

## Connect or update speakers on an existing session

PATCH the content session with the full intended `speaker_assignments` array. There is no separate Core speaker-assignment endpoint, and the speaker resource cannot manage session assignments.


Session speaker assignments: Send every speaker that should remain. Array order determines the assignment position.
Before:

```json
{
  "speaker_assignments": [
    {
      "speaker_id": 801,
      "role": "Moderator"
    },
    {
      "speaker_id": 802,
      "role": "Speaker"
    }
  ]
}
```

**omit** — PATCH body:

```json
{}
```

Conceptual result:

```json
{
  "speaker_assignments": [
    {
      "speaker_id": 801,
      "role": "Moderator"
    },
    {
      "speaker_id": 802,
      "role": "Speaker"
    }
  ]
}
```


**replace** — PATCH body:

```json
{
  "speaker_assignments": [
    {
      "speaker_id": 802,
      "role": "Speaker"
    }
  ]
}
```

Conceptual result:

```json
{
  "speaker_assignments": [
    {
      "speaker_id": 802,
      "role": "Speaker"
    }
  ]
}
```


**clear** — PATCH body:

```json
{
  "speaker_assignments": []
}
```

Conceptual result:

```json
{
  "speaker_assignments": []
}
```



Omit `speaker_assignments` to preserve the existing assignments. Send `[]` only when you intend to remove all of them. Do not send a one-speaker array to append someone to a session that already has speakers.

## Verify, then finish the batch

Read the created session by its returned ID with `include=speaker-assignments,track`. The scope must allow these associations and their fields. Check each speaker ID and role, title, duration, location and the time shown in the organizer agenda.

Once the sample is correct and the remaining plan is approved, process the rest sequentially or with modest bounded concurrency. Keep a per-item log of source ID, request purpose, returned ID and result. Never log the API token.

Each write request has its own transaction; the entire import is not atomic. If a request times out, look up its external ID before retrying. Session and speaker external IDs are unique within an event when present, but POST is not an upsert and a duplicate can fail validation. Stop on an ambiguous match or permission failure, fix the cause, then resume from the recorded results.

There is no general Core session `draft` or `publish` input in this workflow. Treat writes as changes to the event's agenda; a locally reviewed plan is not an unpublished server-side draft.

The [agent guide](https://docs.brella.io/agents/agenda-import) includes a reusable prompt and a structured plan template for this exact task.
