# Meetings

Booked meetings and their outcomes.

Source: https://docs.leadarc.io/reference-meetings.html

- [GET /v1/workspaces/{ws}/meetings](#get-v1-workspaces-ws-meetings)
- [GET /v1/workspaces/{ws}/meetings/{id}](#get-v1-workspaces-ws-meetings-id)
- [POST /v1/workspaces/{ws}/meetings/{id}/outcome](#post-v1-workspaces-ws-meetings-id-outcome)

## GET /v1/workspaces/{ws}/meetings

`meetings:read` · upstream bucket · returns list of meeting

Paginated meeting feed

```bash
curl -sS https://api.leadarc.io/v1/workspaces/airpay/meetings?limit=25 \
  -H "Authorization: Bearer $LEADARC_API_KEY"
```

**Query parameters.** Anything else is [`400 unsupported_filter`](errors.html#status-400).

`limit` `cursor` `order` `outcome` `source` `host_id` `event_type_id` `booked_since` `booked_before` `starts_after` `starts_before`

`starting_after` also works here, but prefer [`cursor`](pagination.html#cursors).

**Errors from this endpoint**, on top of the [common ones](errors.html): [`invalid_cursor`](errors.html#status-400), [`invalid_request`](errors.html#status-400), `unsupported_filter`.

**This endpoint does not reuse `fetchScheduledMeetingsPage`.** That function calls `syncUpcomingMeetingTimes(pageRaw)` after the select, which resolves each host's calendar connection and makes live Google Calendar or Microsoft Graph calls (bounded by `MEETING_TIME_SYNC_CONCURRENCY = 5`), then writes corrected times back to the database. It also calls `ensureMeetingChannelColumn()`, which is DDL. At up to 500 rows a page that is a per-row cost on a per-request budget, and it burns per-user OAuth quota.

So v1 adds `fetchApiMeetingPage` in the app. It keeps the compound `(booked_at, id)` cursor, which is already correct, and drops the sync. The trade is stated here: a meeting moved on the host's own calendar may take up to 15 minutes to show through the API, because the sync then only happens when a human opens the Scheduled Meetings tab or the calendar connector cron runs. That is honest and it is what keeps the endpoint fast.

The endpoint stays on the `upstream` bucket anyway, because a future `?refresh=true` will opt into the sync and the bucket should not change later.
```json
{
  "object": "list",
  "data": [{
    "object": "meeting",
    "id": "mtg_0TSS9xjHfdw6BA9DbWjqCmhNIDwmX5Ys5b9Lpp",
    "workspace": "airpay",
    "lead_id": "lead_BP2vnPmKyttNP2OlH_mWL78t1o6nBVC6oVbHny",
    "event_type_id": "evt_Wu3ZomyygPicrgjIS8BCvQCFto8fHiIno2TeJH",
    "source": "calendar",
    "booked_at": "2026-07-27T09:41:00Z",
    "booked_by_id": "usr_zaDRRpdu8EPZSU3i89lvZgKPkVQo4uBFA_KOhm",
    "host_id": "usr_zaDRRpdu8EPZSU3i89lvZgKPkVQo4uBFA_KOhm",
    "starts_at": "2026-07-30T14:00:00Z",
    "ends_at": "2026-07-30T14:30:00Z",
    "timezone": "America/New_York",
    "invitee": {
      "name": "Mary Adams",
      "email": "m.adams@northgatecare.com",
      "phone": "+15551234567"
    },
    "lead_snapshot": {
      "email": "m.adams@northgatecare.com",
      "name": "Mary Adams",
      "company_name": "Northgate Care",
      "domain": "northgatecare.com"
    },
    "guests": [{ "name": "Tom Reyes", "email": "t.reyes@northgatecare.com" }],
    "custom_fields": { "team_size": "40-80" },
    "utm_params": { "utm_source": "ytl" },
    "channel": "Cold Email",
    "confirmation_status": "confirmed",
    "location": { "type": "google_meet", "phone": null },
    "outcome": null,
    "outcome_at": null,
    "outcome_by_id": null,
    "locked": false,
    "created_at": "2026-07-27T09:41:00Z"
  }],
  "has_more": false,
  "next_cursor": null
}
```
`meet_link` and `internal_notes` are **absent from this shape**. `internal_notes` appears only on `GET.../meetings/{id}` and only when the key holds [`meetings:write`](scopes.html#all). `meet_link` is never returned in v1. Details in the object schema.

## GET /v1/workspaces/{ws}/meetings/{id}

`meetings:read` · read bucket · returns meeting

One meeting

```bash
curl -sS https://api.leadarc.io/v1/workspaces/airpay/meetings/mtg_0TSS9xjHfdw6BA9DbWjqCmhNIDwmX5Ys5b9Lpp \
  -H "Authorization: Bearer $LEADARC_API_KEY"
```

**Errors from this endpoint**, on top of the [common ones](errors.html): `invalid_request`, [`not_found`](errors.html#status-404).

## POST /v1/workspaces/{ws}/meetings/{id}/outcome

`meetings:write` · write bucket · returns meeting

Showed, no-show, cancelled, rescheduled

```bash
curl -sS https://api.leadarc.io/v1/workspaces/airpay/meetings/mtg_0TSS9xjHfdw6BA9DbWjqCmhNIDwmX5Ys5b9Lpp/outcome \
  -X POST \
  -H "Authorization: Bearer $LEADARC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"outcome":"...","force":"..."}'
```

**Body fields.** An unknown field is rejected.

`outcome` `force`

**Errors from this endpoint**, on top of the [common ones](errors.html): [`conflict`](errors.html#status-409), `invalid_request`, `not_found`.

```json
{ "outcome": "showed" }
```
Accepts `showed`, `no_show`, `no_show_host`, `cancelled`, `rescheduled`. Sets `outcome`, `outcome_at`, `outcome_by_id` (the key's owner), and `locked_at`.

**It also mirrors the outcome onto the lead.** `responses_index.meeting_outcome` is a separate axis from `last_action` by design: a no-show or cancelled lead stays `interested` so EmailBison statistics are untouched, while still carrying the outcome tag for filtering. The `effects` object reports both writes.

Note the narrower set on a different table: `meeting_notes.outcome` accepts only `showed`, `no_show`, `rescheduled`, `cancelled`, with no `no_show_host`. `meeting_notes` is a CRM table and is not exposed, so this only matters if the CRM ever returns.
```json
{
  "object": "meeting",
  "id": "mtg_0TSS9xjHfdw6BA9DbWjqCmhNIDwmX5Ys5b9Lpp",
  "outcome": "showed",
  "outcome_at": "2026-07-30T15:02:00Z",
  "locked": true,
  "effects": {
    "meeting_outcome_set": "showed",
    "replies_updated": ["rep_Dk9dw7dVABGNJm9HK9onvZKXLneRRpj9MIB-C3"],
    "provider_writes": []
  }
}
```
---

---

Base URL `https://api.leadarc.io/v1`. Generated from the API source.
