Meetings
List, read, create, update and delete your organization's Meetings.
| Method | Path | Scope | Webhook |
|---|---|---|---|
GET |
/meetings |
meetings:read |
|
GET |
/meetings/{id} |
meetings:read |
|
POST |
/meetings |
meetings:write |
None |
PATCH |
/meetings/{id} |
meetings:write |
None |
DELETE |
/meetings/{id} |
meetings:write |
None |
Meeting changes don't fire webhooks. Related: Meeting comments.
Archived Meetings aren't returned: lists leave them out and GET, PATCH and DELETE return 404 for them.
The JavaScript examples assume a headers object holding your key:
const headers = {
'X-API-Key': process.env.GPS_API_KEY_ID,
'X-API-Secret': process.env.GPS_API_SECRET,
};
The Meeting object#
| Field | Type | Notes |
|---|---|---|
id |
string | Meeting ID. |
title |
string | |
description |
string or null | |
status |
string or null | scheduled, in_progress, paused, completed, cancelled or resolved_async (settled through check-ins, so never held). |
meetingType |
string or null | Free text, for example Weekly Check-in. |
scheduledDate |
string | Scheduled start, ISO 8601 timestamp. |
duration |
integer or null | Planned length in minutes. |
actualStartTime |
string or null | When it started. |
actualEndTime |
string or null | When it ended. |
actualDuration |
integer or null | Actual length in minutes. |
location |
string or null | |
isVirtual |
boolean or null | |
meetingUrl |
string or null | Video call link. |
attendees |
string[] | Member IDs. |
externalAttendees |
string[] | Email addresses of people outside the organization. |
notes |
string or null | |
meetingSummary |
string or null | Summary written after the Meeting. |
keyDecisions |
string[] | The text of each decision recorded in the Meeting. |
participantCount |
integer | How many people took part. |
recordingUrl |
string or null | |
createdBy |
string or null | Member ID of the creator. |
organizationId |
string or null | |
createdAt |
string | ISO 8601 timestamp. |
updatedAt |
string | ISO 8601 timestamp. |
List Meetings#
GET /meetings
Order isn't guaranteed; sort by scheduledDate on your side.
Scope: meetings:read
Query parameters
| Parameter | Type | Notes |
|---|---|---|
status |
string | One of scheduled, in_progress, paused, completed, cancelled, resolved_async. |
limit |
integer | 1–200, default 50. |
offset |
integer | Default 0. |
curl "https://plan.empiraa.app/api/v1/meetings?status=completed" \
-H "X-API-Key: $GPS_API_KEY_ID" \
-H "X-API-Secret: $GPS_API_SECRET"
const res = await fetch('https://plan.empiraa.app/api/v1/meetings?status=completed', { headers });
const { data: meetings, meta } = await res.json();
Response 200
{
"data": [
{
"id": "7a9c1e3f-5b6d-4e8a-8b9c-4d5e6f7a8b9c",
"title": "Monthly strategy review",
"description": "Review revenue Objectives and agree next steps.",
"status": "completed",
"meetingType": "Strategic Planning",
"scheduledDate": "2026-11-05T22:00:00.000Z",
"duration": 60,
"actualStartTime": "2026-11-05T22:02:11.000Z",
"actualEndTime": "2026-11-05T22:58:40.000Z",
"actualDuration": 56,
"location": null,
"isVirtual": true,
"meetingUrl": "https://meet.example.com/abc-defg-hij",
"attendees": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70", "6e8a0c2e-4f6b-4d8f-a0b1-3c4d5e6f7a81"],
"externalAttendees": ["advisor@example.com"],
"notes": "Pricing review moved to December.",
"meetingSummary": "Revenue is on track; churn needs attention.",
"keyDecisions": ["Launch the annual plan discount on 1 December"],
"participantCount": 3,
"recordingUrl": null,
"createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
"organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
"createdAt": "2026-10-29T01:00:00.000Z",
"updatedAt": "2026-11-05T23:00:12.000Z"
}
],
"meta": { "total": 1, "limit": 50, "offset": 0 }
}
Errors: 400 INVALID_STATUS_QUERY for an unknown status; 422 VALIDATION_ERROR for a bad limit or offset.
Get a Meeting#
GET /meetings/{id}
Scope: meetings:read
| Path parameter | Notes |
|---|---|
id |
Meeting ID. |
curl https://plan.empiraa.app/api/v1/meetings/7a9c1e3f-5b6d-4e8a-8b9c-4d5e6f7a8b9c \
-H "X-API-Key: $GPS_API_KEY_ID" \
-H "X-API-Secret: $GPS_API_SECRET"
const res = await fetch('https://plan.empiraa.app/api/v1/meetings/7a9c1e3f-5b6d-4e8a-8b9c-4d5e6f7a8b9c', { headers });
const { data: meeting } = await res.json();
Response 200: { "data": <Meeting> }, with the same fields as the item in List Meetings.
Errors: 404 NOT_FOUND if the Meeting doesn't exist, is in another organization, or is archived.
Create a Meeting#
POST /meetings
Scope: meetings:write · Webhook: none.
Request body
| Field | Type | Required | Allowed values | Notes |
|---|---|---|---|---|
title |
string | Yes | At least 1 character after trimming | |
scheduledDate |
string | Yes | A valid ISO 8601 date-time | Scheduled start. Include a time zone offset. |
description |
string or null | No | ||
duration |
integer or null | No | Minutes. Default 60. | |
attendees |
string[] or null | No | Member IDs | Not checked against your member list, so send IDs from GET /users. |
externalAttendees |
string[] or null | No | Email addresses | |
meetingType |
string or null | No | ||
location |
string or null | No | ||
isVirtual |
boolean or null | No | Default true. |
|
meetingUrl |
string or null | No | ||
status |
string | No | scheduled, in_progress, paused, completed, cancelled, resolved_async |
Default scheduled. |
notes |
string or null | No | ||
onBehalfOf |
string | No | Active member ID | Who the Meeting is created by. See Attribution. |
curl -X POST https://plan.empiraa.app/api/v1/meetings \
-H "X-API-Key: $GPS_API_KEY_ID" \
-H "X-API-Secret: $GPS_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"title": "Monthly strategy review",
"scheduledDate": "2026-12-03T09:00:00+11:00",
"duration": 60,
"meetingType": "Strategic Planning",
"attendees": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
"meetingUrl": "https://meet.example.com/abc-defg-hij"
}'
const res = await fetch('https://plan.empiraa.app/api/v1/meetings', {
method: 'POST',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({
title: 'Monthly strategy review',
scheduledDate: '2026-12-03T09:00:00+11:00',
duration: 60,
meetingType: 'Strategic Planning',
attendees: ['5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70'],
meetingUrl: 'https://meet.example.com/abc-defg-hij',
}),
});
const { data: meeting } = await res.json();
Response 201
{
"data": {
"id": "8b0d2f4a-6c7e-4f9b-9c0d-5e6f7a8b9c0d",
"title": "Monthly strategy review",
"description": null,
"status": "scheduled",
"meetingType": "Strategic Planning",
"scheduledDate": "2026-12-02T22:00:00.000Z",
"duration": 60,
"actualStartTime": null,
"actualEndTime": null,
"actualDuration": null,
"location": null,
"isVirtual": true,
"meetingUrl": "https://meet.example.com/abc-defg-hij",
"attendees": ["5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70"],
"externalAttendees": [],
"notes": null,
"meetingSummary": null,
"keyDecisions": [],
"participantCount": 0,
"recordingUrl": null,
"createdBy": "5d7f9b1d-3e5a-4c7e-9f1a-2b3c4d5e6f70",
"organizationId": "9b2f4e1a-6c3d-4e8f-a1b2-c3d4e5f6a7b8",
"createdAt": "2026-11-12T03:18:00.511Z",
"updatedAt": "2026-11-12T03:18:00.511Z"
}
}
Errors
| Status | code |
When |
|---|---|---|
| 422 | VALIDATION_ERROR |
Missing title or scheduledDate, scheduledDate isn't a valid date, or a value outside the allowed list. |
| 422 | INVALID_USERS |
onBehalfOf isn't an active member. |
| 422 | NO_KEY_OWNER |
No onBehalfOf, and the key's creator is no longer a member. |
Update a Meeting#
PATCH /meetings/{id}
Changes only the fields you send. attendees and externalAttendees replace the whole list.
Scope: meetings:write · Webhook: none.
| Path parameter | Notes |
|---|---|
id |
Meeting ID. |
Request body: any field from Create a Meeting except onBehalfOf, with the same rules. Send at least one field.
curl -X PATCH https://plan.empiraa.app/api/v1/meetings/8b0d2f4a-6c7e-4f9b-9c0d-5e6f7a8b9c0d \
-H "X-API-Key: $GPS_API_KEY_ID" \
-H "X-API-Secret: $GPS_API_SECRET" \
-H "Content-Type: application/json" \
-d '{ "scheduledDate": "2026-12-04T09:00:00+11:00", "location": "Boardroom", "isVirtual": false }'
const res = await fetch('https://plan.empiraa.app/api/v1/meetings/8b0d2f4a-6c7e-4f9b-9c0d-5e6f7a8b9c0d', {
method: 'PATCH',
headers: { ...headers, 'Content-Type': 'application/json' },
body: JSON.stringify({ scheduledDate: '2026-12-04T09:00:00+11:00', location: 'Boardroom', isVirtual: false }),
});
const { data: meeting } = await res.json();
Response 200: { "data": <Meeting> } with the changes applied, for example "scheduledDate": "2026-12-03T22:00:00.000Z", "location": "Boardroom" and "isVirtual": false. Every other field is as in Create a Meeting.
Errors: 404 NOT_FOUND if the Meeting doesn't exist, is in another organization or is archived; 422 VALIDATION_ERROR for an invalid value.
Delete a Meeting#
DELETE /meetings/{id}
Permanently deletes the Meeting.
Scope: meetings:write · Webhook: none.
| Path parameter | Notes |
|---|---|
id |
Meeting ID. |
curl -X DELETE https://plan.empiraa.app/api/v1/meetings/8b0d2f4a-6c7e-4f9b-9c0d-5e6f7a8b9c0d \
-H "X-API-Key: $GPS_API_KEY_ID" \
-H "X-API-Secret: $GPS_API_SECRET"
const res = await fetch('https://plan.empiraa.app/api/v1/meetings/8b0d2f4a-6c7e-4f9b-9c0d-5e6f7a8b9c0d', {
method: 'DELETE',
headers,
});
console.log(res.status); // 204
Response 204: no body.
Errors: 404 NOT_FOUND if the Meeting doesn't exist, is in another organization, or is archived.

