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

# create_collab

> Put an event into heyBTW as a collab, from its details or from its public page link.

> [!WARNING]
>
> Writes to your heyBTW workspace: it creates one collab (and its target spec, if you pass one). It sends nothing, runs no enrichment, and never writes to the event platform.

A collab is the record people, lists and context attach to. Reach for this tool when the user names an event heyBTW does not hold yet, or pastes the link to one.

Follows the [heyBTW MCP rules](https://heybtw.com/docs/mcp/rules.md).

## When to use it

* "Add our Oct 20 dinner in SF to heyBTW, 6 to 9pm."
* "Here's the Luma link for next week's meetup, set it up."
* "We ran a Partiful event last month. Get it into heyBTW so I can load the guest list."
* "Create the collab and say who it's for: VPs of Marketing at Series B fintechs."

## Parameters

Pick ONE door per call.

| Parameter | Type | Required | Notes |
| - | - | - | - |
| `client_request_id` | string | Yes, both doors | A fresh hyphenated UUID (for example a version 4 UUID) for this call. |
| `name` | string | FIELDS door | At most 200 characters. |
| `event_start` | string | FIELDS door | RFC 3339 with an offset (`2026-10-20T18:00:00-07:00`), or a local time (`2026-10-20T18:00`) sent with `timezone`. A time with neither is refused, never guessed. A local time a daylight-saving change skips or repeats is refused with the two times it could mean. |
| `event_end` | string | No | Same rule as `event_start`. Left out, the end is 3 hours after the start and `event_end_origin` is `assumed`. |
| `timezone` | string | No | The event's IANA zone, for example `America/Los_Angeles`. |
| `location` | string | No | One line, at most 120 characters. |
| `landing_page_url` | string | No | The one public page where the event is listed. One https link, at most 2048 characters, no user name or password, no parameter named like a key, token, secret, password or credential. Stored as a pointer: heyBTW never opens it. |
| `page_read_fields` | string\[] | No | With `landing_page_url`: which of `name`, `event_start`, `event_end`, `timezone`, `location` you read from that page yourself. Never list a value the person told you. |
| `event_url` | string | LINK door | Any public event link. Pasting one is always safe. |
| `target_spec` | object | No | The same block save\_target\_spec takes at collab scope. A bad item never fails the create. |
| `contract_version` | string | No | Omit for v1, the only version advertised. |

## Workflow

There is no preview step: one call creates the collab. Retries are safe because of `client_request_id`.

**1. Choose the door**

FIELDS door when you have the details (`name`, `event_start`). LINK door when you have a public page (`event_url`).

**2. Generate a fresh client\_request\_id**

A new hyphenated UUID for every new create. On a transport error, retry with the SAME id: a committed creation is replayed (`idempotent_replay: true`), never repeated. Reusing the id for a different call is refused.

**3. Read the answer to the user**

Read `facts` one sentence each, then offer `next`. If `event_end_origin` is `assumed`, tell the person the end is assumed and set it with update\_collab once known.

**4. Carry on with the channel\_id**

Pass `channel_id` unchanged to record\_signups, get\_event\_details, save\_event\_context, update\_collab and every other tool that takes a collab.

### What the LINK door does

| Link | Result |
| - | - |
| A Luma (luma.com, lu.ma) or Partiful (partiful.com/e/...) event the workspace does not hold | `created` from the page's public details, never its guest list. A page with no end gets the assumed end. A Partiful page that was deleted, needs sign-in or states no readable details is refused, naming that. |
| A link for a collab the workspace already holds (its Luma event, an earlier paste, its `landing_page_url`) | `matched`. Empty, assumed and page\_read fields are filled from the page; `fields` says per field `filled`, `kept` or `unavailable`. A value a person stated is never changed. |
| A link heyBTW cannot read yet that no collab holds | Refused, nothing created. The refusal names the next call: the FIELDS door with `landing_page_url` set to that link. Read the public page yourself if you can and pass what it states with `page_read_fields`. |

The FIELDS door is `matched` too when another collab already has its `landing_page_url`: that collab comes back as it is, and the values in the call are not applied (use update\_collab to change them). A name and a start alone never match.

## The event-history ladder

A collab created here sits on rung 2 of the ladder: it brings the event, not its people or partners. Rung 1, an API sync from a connected Luma, Splash or Zuddl account, is better whenever it exists. Every answer lists what this rung leaves out in `import_gaps`, each with the tool that fills it:

| gap | remedy\_tool |
| - | - |
| `partners` | manage\_collab\_partners |
| `attendance` | record\_signups |
| `registration_answers` | record\_signups |

A collab the Luma integration already synced reports the rung 1 gaps instead (`partners` only).

## Response

The shape below is from the contract and the code (`tools/create_collab.py`, `collab-write-contract.json`). A success was not captured live, because this tool creates a real collab. `data[0]` opens with the first-value block:

| Field | Meaning |
| - | - |
| `facts` | In order: registered, seen\_before, target\_accounts, unresolved\_people, known\_by, gap\_to\_target, registered\_pipeline. Each `{id, sentence, fields, via, basis}`, or `{id, gap}` when absent. `registered_pipeline` appears only when a CRM is connected inside heyBTW. |
| `carry` | `{destination, via: your_tools, join_keys, ask}`: join on `email` and `company_domain`, the question to put to the user's own CRM connector. |
| `next` | The one step for the collab's event window, or a gap saying why there is none yet. |
| `channel_id` | The collab handle. |
| `event_name`, `event_date`, `event_end` | Name, start and end (UTC). |
| `event_end_origin` | `assumed` (start plus 3 hours) or `user_stated`; null when not recorded. |
| `field_origins` | Per field: `page_read`, `user_stated`, `assumed`, or null. |
| `event_tense` | `upcoming`, `live` or `past`, read exactly as list\_events reads it. |
| `timezone`, `location`, `landing_page_url` | As stored. |
| `outcome` | `created`, or `matched` when the workspace already holds the event. |
| `door` | `fields` or `url`. |
| `source` | `{provider, url}` for a page the LINK door read (`luma` or `partiful`), else null. |
| `sync_state` | `api_synced` or `public_metadata_only`. |
| `fields` | On a `matched` LINK answer: per field `filled`, `kept` or `unavailable`. Null otherwise. |
| `possible_duplicates` | Live collabs with the same name and start (id, event\_name, event\_date), never merged. |
| `idempotent_replay` | true when this answer replays an earlier committed call. |
| `import_gaps` | The ladder gaps above. |
| `target_spec` | Present when you passed one: what was saved and any item left out. |

A brand-new collab has no sign-ups, so most facts read 0 or a gap. An empty roster says nothing about who registered.

## Rules the agent must follow

* Pick one door per call. Never send `event_name` or `event_date` on input: those are response names.
* Never guess a time zone or an offset. Ask the user.
* Read `facts` to the user before proposing anything, then offer `next`. Never recount from the roster. A gap is absent, not zero.
* If `possible_duplicates` is not empty, confirm with the person which collab is the real event before adding people to either.
* People who registered or attended go to record\_signups. People to invite go to import\_people, save\_list, activate\_list: they are not sign-ups.
* An archived match stays archived; a coverage note says so. Tell the user.

## Errors and gaps

| Code | What to do |
| - | - |
| `invalid_parameter` | Validation lists every missing or bad parameter in one error ("The list is complete"). Fix all of them and call again. |
| `collab_limit_reached` | The workspace is at its active-collab limit. Nothing was created. Archiving a collab frees a slot. |
| `insufficient_scope` | The OAuth token lacks `mcp:tools:write`. Nothing was created. Reconnect with write access. |
| `internal_error` | Retry once with the SAME `client_request_id`. |
| 429 | Honor `Retry-After`. Limits: 30 requests per 5 s, 60 per minute, 1000 per day per workspace. |

The validation error, trimmed from a real call with no arguments today:

```json theme={null}
{
  "tool": "create_collab",
  "status": "error",
  "data": null,
  "error": {
    "code": "invalid_parameter",
    "what_failed": "A tool call has parameter problems. Missing: `client_request_id`, `name` and `event_start`.",
    "what_to_do": "Fix every parameter listed, then call again. The list is complete. ... On the URL door `create_collab` requires `event_url` and `client_request_id` instead. `event_name` and `event_date` are names in the response and are not accepted on input: send `name` and `event_start`. Nothing ran.",
    "retryable": false
  }
}
```

## Related

* [update\_collab](https://heybtw.com/docs/tools/update-collab.md), to fix an assumed end or rename
* [record\_signups](https://heybtw.com/docs/tools/record-signups.md), the next step for a past event
* [manage\_collab\_partners](https://heybtw.com/docs/tools/manage-collab-partners.md), to fill the `partners` gap
* Playbook: [Record who registered and who came](https://heybtw.com/docs/playbooks/record-who-came.md)


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