Skip to main content
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

GET /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

GET /calendar/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

ParamRequiredNotes
userIdyes (API key)The member whose calendar to read. Optional for OAuth callers (defaults to token owner).
timeMinyesLower bound, ISO 8601 with offset, e.g. 2026-06-20T00:00:00-05:00.
timeMaxyesUpper 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

FieldDescription
idGoogle Calendar event id. Use it on PATCH / DELETE.
titleEvent summary. Falls back to (sin título) when Google has no summary.
descriptionOptional free text. Omitted when empty.
start, endISO 8601 datetime, or YYYY-MM-DD for all-day events.
allDaytrue for date-only (all-day) events.
locationOptional. Omitted when not set.
htmlLinkLink to the event in Google Calendar. Optional.
attendeesArray of attendee emails. Omitted when there are none.

Common errors

  • 400 BAD_REQUESTuserId is missing (API-key callers must pass it).
  • 400 VALIDATION_ERRORtimeMin / 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_MEMBERuserId is not a member of this organization.

Schedule a meeting

POST /calendar/events
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"
}
FieldRequiredNotes
userIdyes (API key)The member whose calendar to use. Optional for OAuth callers.
leadIdyesUUID of the lead. Must be a lead the acting member can access.
startAtyesLocal 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.
durationMinutesyesInteger, 5600. The end time is derived from startAt + duration.
titleno1–300 chars. Defaults to Reunión con {lead name}.
descriptionno≤ 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_REQUESTuserId 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_MEMBERuserId 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"
}
FieldRequiredNotes
userIdyes (API key)The member whose calendar to use. Optional for OAuth callers.
startAtyesLocal wall-clock datetime YYYY-MM-DDTHH:mm, interpreted in the organization timezone.
durationMinutesyesInteger, 5600.
titleyes1–300 chars.
descriptionno≤ 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_REQUESTuserId 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_MEMBERuserId 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

ParamRequiredNotes
userIdyes (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

{ "success": true }

Common errors

  • 400 BAD_REQUESTuserId 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_MEMBERuserId is not a member of this organization.