Read-only JSON API over the SingularityNET Ambassador Program meeting archive. Every endpoint supports filtering, sorting and pagination.
Base URL: https://archives-dashboard.netlify.app/api/v1
Every request needs the server API key. Send it in one of these ways:
x-api-key: <key>
Authorization: Bearer <key>
The legacy api_key header is also accepted. The key is the value of the
SERVER_API_KEY environment variable on the server. It is a secret: never
embed it in client-side code.
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_api_key |
No key was sent |
| 403 | invalid_api_key |
The key does not match |
| 500 | server_misconfigured |
SERVER_API_KEY is not set on the server |
Note that the dashboard pages (/search, /charts) render the same data
without a key. The key protects the API surface, not the dataset itself.
The dashboard's own /api/search endpoint is internal: it is unversioned,
same-origin only, needs no key, and is not part of the /api/v1 contract.
Successful responses:
{
"data": [ ... ],
"meta": {
"total": 1234,
"limit": 50,
"offset": 0,
"hasMore": true,
"loadedAt": "2026-09-25T10:15:00.000Z"
}
}loadedAt is when the server last loaded the dataset from the database. The
server caches the dataset in memory for five minutes.
Errors:
{ "error": { "code": "invalid_param", "message": "limit must be an integer between 1 and 500" } }| Status | Code |
|---|---|
| 400 | invalid_param |
| 404 | not_found |
| 405 | method_not_allowed |
| 500 | internal_error |
Only GET and OPTIONS are allowed. CORS is open (*). Responses are sent
with Cache-Control: private, no-store.
/meetings returns rows in this shape. Fields inside summary are only
present when the documenter filled them in, so treat everything except
workgroup, workgroup_id, meetingInfo, type, canceledSummary and
noSummaryGiven as optional. Even agendaItems is absent on cancelled or
no-summary meetings. The example below shows every field observed across the
archive; a typical row has far fewer.
{
"meeting_id": "ff4d5ef9-3354-4192-86b9-e5e66601cc8f",
"created_at": "2026-01-12T19:51:16.20138+00:00",
"updated_at": "2026-02-10T13:29:25.625+00:00",
"confirmed": true,
"workgroup_id": "649a933f-3067-468f-891e-dbe47adcb88a",
"name": "Monthly",
"date": "2026-01-12T00:00:00",
"summary": {
"workgroup": "AI Ethics WG",
"workgroup_id": "649a933f-3067-468f-891e-dbe47adcb88a",
"type": "custom",
"meetingInfo": {
"name": "Monthly",
"date": "2026-01-12",
"host": "CallyFromAuron",
"documenter": "CallyFromAuron",
"peoplePresent": "CallyFromAuron, UKnowZork, PeterE",
"purpose": "Monthly meeting of the AI Ethics Workgroup",
"workingDocs": [
{ "title": "AI Ethics WorkGroup Github board", "link": "https://github.com/..." }
],
"meetingVideoLink": "https://...",
"miroBoardLink": "https://...",
"otherMediaLink": "https://...",
"transcriptLink": "https://...",
"mediaLink": "https://...",
"googleSlides": "https://...",
"townHallNumber": "42",
"timestampedVideo": {
"url": "https://...",
"intro": "Opening remarks",
"timestamps": "00:00 Intro\n05:12 Updates",
"sections": [ { "title": "Intro", "content": "..." } ]
}
},
"agendaItems": [
{
"agenda": "Unconference recordings",
"status": "carry over",
"narrative": "Free-text summary of the discussion, may contain markdown",
"discussion": "Older free-text field, rarely used",
"discussionPoints": [ "Point one", "Point two" ],
"meetingTopics": [ "Topic" ],
"issues": [ "Issue" ],
"learningPoints": [ "Lesson" ],
"townHallUpdates": "...",
"townHallSummary": "...",
"gameRules": "...",
"actionItems": [
{
"text": "Vani and Tina to work on the AI And Ecology session",
"assignee": "CallyFromAuron, Tina Shriver",
"dueDate": "26 January 2026",
"status": "in progress"
}
],
"decisionItems": [
{
"decision": "Introduction to qualitative coding session with Esther",
"rationale": "Why the decision was taken",
"opposing": "Arguments raised against it, if any",
"effect": "mayAffectOtherPeople"
}
]
}
],
"tags": {
"topicsCovered": "interview transcription, interviews, transcription backlog",
"emotions": "forward-looking, organised, quiet",
"gamesPlayed": "Only used by the gaming workgroups",
"other": "..."
},
"noSummaryGiven": false,
"noSummaryGivenText": "No Summary Given",
"canceledSummary": false,
"canceledSummaryText": "Meeting was cancelled"
}
}Field notes:
| Field | Notes |
|---|---|
meeting_id |
Stable unique id. Use it with /meetings/{meeting_id}. |
confirmed |
true for archived summaries. false rows are recent drafts with no archived version yet. |
date |
Row-level timestamp of the meeting date. Prefer summary.meetingInfo.date, which is a plain YYYY-MM-DD. |
summary.type |
Summary template. Currently custom, though casing varies (Custom also occurs). The type filter is case-insensitive. |
summary.meetingInfo.peoplePresent |
Comma-separated names in one string. |
summary.meetingInfo.workingDocs |
Present on most rows. Links, slides and video fields are present only when provided. |
summary.agendaItems |
Absent when canceledSummary or noSummaryGiven is true. |
summary.agendaItems[].status |
carry over or resolved. |
summary.agendaItems[].actionItems[].text |
Occasionally missing. /action-items skips items with no text. |
summary.agendaItems[].actionItems[].assignee |
Comma-separated names in one string. The assignee filter matches any one of them. |
summary.agendaItems[].actionItems[].dueDate |
Human-readable, for example 26 January 2026. Not ISO. Use the due* filters on /action-items for date comparisons. |
summary.agendaItems[].actionItems[].status |
Lower-case, for example todo, in progress, done, cancelled. /facets lists the values in use. |
summary.agendaItems[].decisionItems[].effect |
affectsOnlyThisWorkgroup or mayAffectOtherPeople, or absent. |
summary.agendaItems[].decisionItems[].rationale / opposing |
Optional free text. |
summary.tags |
Absent on some rows. Each value is a comma-separated string, not an array. topicsCovered and emotions are the common ones; gamesPlayed and other are rare. The tag filter matches any single entry across all of them. |
summary.canceledSummary / noSummaryGiven |
When true, the meeting was cancelled or no summary was written and agendaItems will be empty. |
Each workgroup writes its summaries from its own meeting template, so which
agendaItems and meetingInfo fields are filled in depends on the workgroup
more than on the meeting. Do not assume a field is present because another
workgroup's rows have it. The main patterns in the archive:
| Fields | Used by |
|---|---|
actionItems, decisionItems, discussionPoints, tags.topicsCovered, tags.emotions, workingDocs |
Most workgroups. This is the common core. |
agendaItems[].agenda, meetingInfo.miroBoardLink |
Treasury Guild, Treasury Automation WG, Treasury Policy WG, Knowledge Base Workgroup, Dework PBL, GitHub PBL WG. These groups structure the summary by agenda item title. |
agendaItems[].narrative |
African Guild, AI Ethics WG, Governance Workgroup, LatAm Guild, Strategy Guild, Writers Workgroup. A free-text write-up instead of, or alongside, bullet points. |
agendaItems[].townHallSummary, meetingInfo.townHallNumber, meetingInfo.timestampedVideo |
Ambassador Town Hall and Deep Funding Town Hall. Town halls are recorded, so most rows carry a video URL with timestamped sections. |
agendaItems[].gameRules, tags.gamesPlayed |
Gamers Guild only. |
agendaItems[].meetingTopics |
Research and Development Guild, Education Workgroup, WG Sync Call. |
agendaItems[].issues, agendaItems[].learningPoints, agendaItems[].townHallUpdates |
Onboarding Workgroup, occasionally Writers Workgroup. |
agendaItems[].discussion |
WG Sync Call only. An older free-text field. |
meetingInfo.meetingVideoLink |
Archives Workgroup on almost every row; scattered elsewhere. |
tags.other |
Rare, mostly LatAm Guild. |
meetingInfo.name is the meeting cadence rather than a title: Weekly,
Biweekly, Monthly or One-off event. A few workgroups use their own
labels such as Think-Tank or Sandbox.
When writing a client, read the fields you need with a fallback for absence,
and use the q search (which looks across every text field) rather than
targeting one field if you want results from all workgroups.
/action-items and /decisions return the nested items flattened into one
row each, with workgroup, workgroup_id, meeting_id and the meeting date
added. See those sections for their fields.
These apply to /meetings, /action-items and /decisions.
| Param | Type | Description |
|---|---|---|
q |
string | Case-insensitive substring search. See each endpoint for the fields searched. |
workgroup |
string | Matches the workgroup_id exactly, or the workgroup name case-insensitively. Call /api/v1/facets for the list of valid ids and names. |
date |
YYYY-MM-DD |
Exact meeting date. |
dateFrom |
YYYY-MM-DD |
Inclusive lower bound on the meeting date. |
dateTo |
YYYY-MM-DD |
Inclusive upper bound on the meeting date. |
sort |
enum | Sort field. Options differ per endpoint. |
order |
asc | desc |
Sort direction. Default desc. |
limit |
integer | Page size, 1 to 500. Default 50. |
offset |
integer | Number of items to skip. Default 0. |
Unknown parameters are ignored. Invalid values return 400.
Returns full meeting summary rows (meeting_id, created_at, updated_at,
confirmed, workgroup_id, name, date, and the summary JSON with
agenda items, action items, decisions, meeting info and tags).
| Param | Description |
|---|---|
q |
Searches tags and content: workgroup, meeting name, purpose, host, documenter, people present, agenda narratives, discussion points, topics, issues, learning points, action item text, decisions, and timestamped video notes. When set, each result includes matches: { tags, content } with the number of hits. |
tag |
Case-insensitive match against any tag in topicsCovered, emotions or other. |
type |
Case-insensitive match on summary.type. |
confirmed |
true or false. Confirmed rows are archived summaries. Unconfirmed rows are drafts from the last three months with no archived version. |
host |
Case-insensitive match on the meeting host. |
assignee |
Meetings containing at least one action item assigned to this person. |
sort |
date (default) or updated_at. |
curl -H "x-api-key: $KEY" \
"https://archives-dashboard.netlify.app/api/v1/meetings?q=treasury&workgroup=Archives%20Workgroup&dateFrom=2025-01-01&limit=10"Returns a single meeting summary in data. 404 if the id is unknown.
curl -H "x-api-key: $KEY" "https://archives-dashboard.netlify.app/api/v1/meetings/<meeting_id>"Returns action items flattened out of every meeting. Each item has text,
assignee, dueDate, status, workgroup, workgroup_id, meeting_id and
meetingDate.
| Param | Description |
|---|---|
q |
Searches text, assignee, workgroup and status. |
status |
Case-insensitive match, for example in progress. |
assignee |
Case-insensitive exact match against any name in a comma-separated assignee list. |
due |
Exact due date, YYYY-MM-DD. |
dueFrom |
Inclusive lower bound on the due date. |
dueTo |
Inclusive upper bound on the due date. |
sort |
dueDate (default) or date (meeting date). |
Items with no due date are excluded whenever a due* filter is set.
curl -H "x-api-key: $KEY" \
"https://archives-dashboard.netlify.app/api/v1/action-items?status=in%20progress&dueFrom=2025-01-01&sort=dueDate&order=asc"Returns decisions flattened out of every meeting. Each has decision,
effect, rationale, opposing, workgroup, workgroup_id, date and
meeting_id.
meta.stats carries totals over the whole filtered set:
"stats": { "total": 42, "withRationale": 30, "withEffect": 40, "rationalePercentage": 71, "effectPercentage": 95 }| Param | Description |
|---|---|
q |
Searches decision, effect, rationale and workgroup. |
effect |
Exact match, usually affectsOnlyThisWorkgroup or mayAffectOtherPeople. |
sort |
date only. |
curl -H "x-api-key: $KEY" \
"https://archives-dashboard.netlify.app/api/v1/decisions?effect=mayAffectOtherPeople&q=budget"Returns the distinct values available for each filter, with counts, so a
client can build dropdowns. workgroups lists every workgroup with its id
and display name; either can be used as the workgroup filter on the other
endpoints, and names are matched case-insensitively. statuses, assignees,
effects, tags and types give the valid values for those filters.
{
"data": {
"workgroups": [ { "id": "...", "name": "Archives Workgroup", "count": 120 } ],
"statuses": [ { "value": "in progress", "label": "In Progress", "count": 15 } ],
"assignees": [ { "value": "jane", "label": "Jane", "count": 8 } ],
"effects": [ { "value": "mayAffectOtherPeople", "label": "mayAffectOtherPeople", "count": 40 } ],
"tags": [ { "value": "governance", "label": "governance", "count": 33 } ],
"types": [ { "value": "custom", "label": "Custom", "count": 200 } ]
},
"meta": { "loadedAt": "..." }
}# list the workgroup names, then filter by one (URL-encode spaces)
curl -H "x-api-key: $KEY" "https://archives-dashboard.netlify.app/api/v1/facets"
curl -H "x-api-key: $KEY" "https://archives-dashboard.netlify.app/api/v1/meetings?workgroup=Archives%20Workgroup"Page size is capped at 500. To download the whole archive, step offset by
limit until meta.hasMore is false:
curl -H "x-api-key: $KEY" "https://archives-dashboard.netlify.app/api/v1/meetings?limit=500&offset=0"
curl -H "x-api-key: $KEY" "https://archives-dashboard.netlify.app/api/v1/meetings?limit=500&offset=500"
# continue while meta.hasMore is truemeta.total on the first response tells you how many pages to expect. Add
confirmed=true to fetch only archived summaries.
With 1003 meetings in the archive, the first page looks like this
(summary objects abbreviated):
{
"data": [
{
"meeting_id": "ff4d5ef9-3354-4192-86b9-e5e66601cc8f",
"created_at": "2026-01-12T19:51:16.20138+00:00",
"updated_at": "2026-02-10T13:29:25.625+00:00",
"confirmed": true,
"workgroup_id": "649a933f-3067-468f-891e-dbe47adcb88a",
"name": "Monthly",
"date": "2026-01-12T00:00:00",
"summary": { "workgroup": "AI Ethics WG", "meetingInfo": { "date": "2026-01-12" }, "agendaItems": [ ... ] }
},
{
"meeting_id": "0a1b2c3d-...",
"confirmed": true,
"workgroup_id": "2d56e7d8-52ba-4be0-837f-a9a14ae4dc6e",
"name": "Weekly",
"date": "2026-01-10T00:00:00",
"summary": { "workgroup": "African Guild", "meetingInfo": { "date": "2026-01-10" }, "agendaItems": [ ... ] }
}
// ... 498 more
],
"meta": { "total": 1003, "limit": 500, "offset": 0, "hasMore": true, "loadedAt": "2026-09-25T06:55:47.075Z" }
}The second page (offset=500) has the same shape with "offset": 500 and
"hasMore": true. The third (offset=1000) returns the last three rows:
{
"data": [ { ... }, { ... }, { ... } ],
"meta": { "total": 1003, "limit": 500, "offset": 1000, "hasMore": false, "loadedAt": "2026-09-25T06:55:47.075Z" }
}Concatenating the three data arrays gives all 1003 meetings, ordered by
meeting date, newest first. Each item is the object described under "Meeting
summary object" above. The same applies to /action-items and /decisions.