The Calendar API operates on a specific organization member’s connected Google Calendar. It is member-scoped: every event read or written acts on one member’s primary Google Calendar.
Because API keys are organization-scoped (not tied to a single user), the caller must identify the acting member with a userId on every event call. That member must have connected Google Calendar inside FlowEstate.
Member resolution. For API-key callers, userId is required — pass the id of the member whose calendar to use. For OAuth-token callers, userId is optional and defaults to the token’s owner. If userId is supplied but is not a member of the authenticated organization, the API returns 404 INVALID_MEMBER. If the resolved member has not connected (or has disconnected) Google Calendar, the API returns 403 FORBIDDEN with a reconnect message.
Scheduling a meeting respects the same lead-visibility rules as the rest of the API: a member can only schedule on leads they are allowed to access.
Get the calendar timezone
Scope: calendar:read. Returns the organization’s IANA timezone. Use it to render and interpret the local wall-clock times accepted by the event endpoints. This call is org-scoped and does not require userId.
Request
curl https://panel.flowestate.app/api/v1/calendar/timezone \
-H "Authorization: Bearer fe_k_your_key_here"
Response 200
{ "timezone": "America/Panama" }
When the organization has no timezone configured, FlowEstate falls back to America/Panama.
List events
Scope: calendar:read. Lists the acting member’s primary-calendar events within a time window. Recurring events are expanded into individual instances and ordered by start time.
Query parameters
| Param | Required | Notes |
|---|
userId | yes (API key) | The member whose calendar to read. Optional for OAuth callers (defaults to token owner). |
timeMin | yes | Lower bound, ISO 8601 with offset, e.g. 2026-06-20T00:00:00-05:00. |
timeMax | yes | Upper bound, ISO 8601 with offset, e.g. 2026-06-21T00:00:00-05:00. |
Request
curl -G https://panel.flowestate.app/api/v1/calendar/events \
-H "Authorization: Bearer fe_k_your_key_here" \
--data-urlencode "userId=user_..." \
--data-urlencode "timeMin=2026-06-20T00:00:00-05:00" \
--data-urlencode "timeMax=2026-06-21T00:00:00-05:00"
Response 200
{
"events": [
{
"id": "abcd1234efgh5678",
"title": "Meeting with Ada Lovelace",
"description": "Discuss unit availability",
"start": "2026-06-20T15:00:00-05:00",
"end": "2026-06-20T15:30:00-05:00",
"allDay": false,
"location": "Sales office",
"htmlLink": "https://www.google.com/calendar/event?eid=...",
"attendees": ["ada@example.com"]
}
]
}
Event fields
| Field | Description |
|---|
id | Google Calendar event id. Use it on PATCH / DELETE. |
title | Event summary. Falls back to (sin título) when Google has no summary. |
description | Optional free text. Omitted when empty. |
start, end | ISO 8601 datetime, or YYYY-MM-DD for all-day events. |
allDay | true for date-only (all-day) events. |
location | Optional. Omitted when not set. |
htmlLink | Link to the event in Google Calendar. Optional. |
attendees | Array of attendee emails. Omitted when there are none. |
Common errors
400 BAD_REQUEST — userId is missing (API-key callers must pass it).
400 VALIDATION_ERROR — timeMin / timeMax missing or not ISO 8601 with offset.
403 FORBIDDEN — the member has not connected Google Calendar, or their authorization expired. The message asks them to reconnect their calendar in FlowEstate.
404 INVALID_MEMBER — userId is not a member of this organization.
Schedule a meeting
Scope: calendar:write. Creates a Google Calendar event on the member’s primary calendar and logs a meeting activity on the lead’s timeline. The lead’s email (when present) is added as an attendee, and Google sends invitations to all attendees.
Request body
{
"userId": "user_...",
"leadId": "uuid",
"startAt": "2026-06-20T15:00",
"durationMinutes": 30,
"title": "Meeting with Ada Lovelace",
"description": "Discuss unit availability"
}
| Field | Required | Notes |
|---|
userId | yes (API key) | The member whose calendar to use. Optional for OAuth callers. |
leadId | yes | UUID of the lead. Must be a lead the acting member can access. |
startAt | yes | Local wall-clock datetime YYYY-MM-DDTHH:mm (no offset). Interpreted in the organization timezone, so 15:00 means 3pm org-local regardless of the caller’s timezone. |
durationMinutes | yes | Integer, 5–600. The end time is derived from startAt + duration. |
title | no | 1–300 chars. Defaults to Reunión con {lead name}. |
description | no | ≤ 5000 chars. Stored as the meeting activity’s note. |
Use GET /calendar/timezone to confirm the organization timezone, then send startAt as a naive local time. Do not include an offset — that is added by the server.
Response 201
{
"event": {
"id": "abcd1234efgh5678",
"title": "Meeting with Ada Lovelace",
"description": "Discuss unit availability",
"start": "2026-06-20T15:00:00-05:00",
"end": "2026-06-20T15:30:00-05:00",
"allDay": false,
"htmlLink": "https://www.google.com/calendar/event?eid=...",
"attendees": ["ada@example.com"]
},
"activityId": "act_..."
}
activityId is the id of the meeting activity logged on the lead. It may be absent if the activity could not be persisted.
Common errors
400 BAD_REQUEST — userId is missing (API-key callers must pass it).
400 VALIDATION_ERROR — invalid leadId, malformed startAt, or durationMinutes out of range.
403 FORBIDDEN — the member has not connected Google Calendar, or their authorization expired. The message asks them to reconnect their calendar.
404 INVALID_MEMBER — userId is not a member of this organization.
404 NOT_FOUND — the lead does not exist or is not visible to the acting member.
Webhook side effects
A lead.communication_logged event is dispatched (a meeting, outbound), mirroring a logged communication so external integrations also see the booked meeting.
Update an event
PATCH /calendar/events/{eventId}
Scope: calendar:write. Updates the member’s event. Only the fields you send are changed; existing attendees are preserved. When the event is linked to a lead’s meeting activity, FlowEstate keeps that activity’s time and note in sync (best effort).
Request body
{
"userId": "user_...",
"startAt": "2026-06-20T16:00",
"durationMinutes": 45,
"title": "Meeting with Ada Lovelace (rescheduled)",
"description": "Pushed back 1 hour"
}
| Field | Required | Notes |
|---|
userId | yes (API key) | The member whose calendar to use. Optional for OAuth callers. |
startAt | yes | Local wall-clock datetime YYYY-MM-DDTHH:mm, interpreted in the organization timezone. |
durationMinutes | yes | Integer, 5–600. |
title | yes | 1–300 chars. |
description | no | ≤ 5000 chars. When provided, it overwrites the linked activity’s note; omit it to leave the note untouched. |
eventId comes from the path — do not include it in the body.
Response 200
{
"event": {
"id": "abcd1234efgh5678",
"title": "Meeting with Ada Lovelace (rescheduled)",
"description": "Pushed back 1 hour",
"start": "2026-06-20T16:00:00-05:00",
"end": "2026-06-20T16:45:00-05:00",
"allDay": false,
"htmlLink": "https://www.google.com/calendar/event?eid=...",
"attendees": ["ada@example.com"]
}
}
Common errors
400 BAD_REQUEST — userId is missing (API-key callers must pass it).
400 VALIDATION_ERROR — missing title, malformed startAt, or durationMinutes out of range.
403 FORBIDDEN — the member has not connected Google Calendar, or their authorization expired. The message asks them to reconnect their calendar.
404 INVALID_MEMBER — userId is not a member of this organization.
Delete an event
DELETE /calendar/events/{eventId}
Scope: calendar:write. Deletes the member’s event from Google Calendar and removes the linked lead meeting activity, if any. Deleting an already-deleted event is treated as success.
Query parameters
| Param | Required | Notes |
|---|
userId | yes (API key) | The member whose calendar to use. Optional for OAuth callers. |
Request
curl -X DELETE -G https://panel.flowestate.app/api/v1/calendar/events/abcd1234efgh5678 \
-H "Authorization: Bearer fe_k_your_key_here" \
--data-urlencode "userId=user_..."
Response 200
Common errors
400 BAD_REQUEST — userId is missing (API-key callers must pass it).
403 FORBIDDEN — the member has not connected Google Calendar, or their authorization expired. The message asks them to reconnect their calendar.
404 INVALID_MEMBER — userId is not a member of this organization.