Groups API
Create and manage mind groups for organizing AI personas and building panels. List, create, update, and delete groups and manage their members via the API.
Groups allow you to organize your AI minds (personas) into collections for use in panels, collaborative work, and easier management. A group can contain any minds you own or have access to, and can be used to survey multiple personas at once via the Panels API.
Base URL: https://getminds.ai/api/v1 or https://api.getminds.ai/v1
Concepts
| Concept | Description |
|---|---|
| Group | A collection of AI minds organized together (e.g., "Gen Z Users", "Senior Developers") |
| Group Member | A mind that belongs to a group |
| Owner | The user who created the group and has full control over it |
| Plan Limits | Free (1 group, 3 members), Premium/Team (unlimited) |
List Groups
Retrieve all groups visible to the authenticated user, including owned groups, public groups, and groups shared with you.
Endpoint: GET /api/v1/groups
Headers:
Authorization: Bearer minds_your_api_key
Response
{
"data": [
{
"id": "group-123",
"name": "Gen Z Consumers",
"sparkCount": 5,
"sparks": [
{
"id": "spark-1",
"name": "Emma",
"discipline": "College Student",
"profileImageUrl": "https://..."
},
{
"id": "spark-2",
"name": "Marcus",
"discipline": "Social Media Manager",
"profileImageUrl": "https://..."
}
],
"createdAt": "2025-12-10T12:00:00.000Z",
"updatedAt": "2025-12-15T14:30:00.000Z",
"isPublic": false,
"currentMemberRole": "owner",
"isSharedWithTeam": false,
"isLinkSharingEnabled": false,
"publicShareId": null
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique group identifier |
name | string | Group name |
sparkCount | number | Number of minds in the group |
sparks | array | Full mind details for all group members |
sparks[].id | string | Spark ID |
sparks[].name | string | Spark name |
sparks[].discipline | string | Spark discipline/role |
sparks[].profileImageUrl | string | Profile image URL |
createdAt | string | ISO 8601 creation timestamp |
updatedAt | string | ISO 8601 last update timestamp |
isPublic | boolean | Whether group is publicly visible |
currentMemberRole | string | Your role: "owner", "admin", "member", "team_member", or "public_viewer" |
isSharedWithTeam | boolean | Whether shared with your team (owner only) |
isLinkSharingEnabled | boolean | Whether link sharing is enabled (owner only) |
publicShareId | string | Public share ID if link sharing enabled (owner only) |
Example Request
curl -X GET "https://getminds.ai/api/v1/groups" \
-H "Authorization: Bearer minds_your_api_key"
Create Group
Create a new group with an optional initial set of minds.
Endpoint: POST /api/v1/groups
Headers:
Authorization: Bearer minds_your_api_key
Content-Type: application/json
Request Body
{
"name": "Product Beta Testers",
"sparkIds": ["spark-1", "spark-2", "spark-3"]
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Name of the group (max 255 characters) |
sparkIds | array | No | Array of mind IDs to add as initial members |
Response
{
"data": {
"id": "group-456",
"name": "Product Beta Testers",
"sparkCount": 3,
"sparks": [
{
"id": "spark-1",
"name": "Alex",
"discipline": "Early Adopter",
"profileImageUrl": "https://..."
},
{
"id": "spark-2",
"name": "Jordan",
"discipline": "Tech Enthusiast",
"profileImageUrl": "https://..."
},
{
"id": "spark-3",
"name": "Taylor",
"discipline": "UX Researcher",
"profileImageUrl": "https://..."
}
],
"createdAt": "2025-12-16T10:00:00.000Z",
"updatedAt": "2025-12-16T10:00:00.000Z",
"isPublic": false,
"isSharedWithTeam": false,
"isLinkSharingEnabled": false,
"publicShareId": null,
"currentMemberRole": "owner"
}
}
Example Request
curl -X POST "https://getminds.ai/api/v1/groups" \
-H "Authorization: Bearer minds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Personas",
"sparkIds": ["spark-123", "spark-456"]
}'
Error Responses
400 Bad Request - Missing name or invalid spark IDs
{
"statusCode": 400,
"message": "name is required"
}
{
"statusCode": 404,
"message": "Sparks not found: 1f2e3d4c-..."
}
403 Forbidden - Plan limit reached
{
"statusCode": 403,
"message": "Free plan allows 1 Group. Upgrade for more!",
"data": {
"code": "PLAN_LIMIT",
"limitType": "groups",
"currentPlan": "free",
"limit": 1,
"current": 1
}
}
Create a Group from professional research files
The webapp treats uploaded research material as one study rather than as unrelated attachments. Manual creation, Quick mode, and guided onboarding can combine arbitrary PDF, DOCX, XLS, XLSX, and CSV inputs. The server classifies files by content, extracts audience definitions, target and achieved distributions, quotas, routing or termination rules, conditional bases, multi-select variables, derived segments, and supported mappings between a research specification and respondent datasets.
Classification separates screeners and unfielded questionnaires from completed study reports and respondent-level datasets. A screener contributes its complete candidate answer universe, eligibility rules, source-excluded options, and explicit target quotas; it does not turn an answer list into observed population data. When a screener and respondent data describe the same axis, observed shares are reconciled with the screener's eligibility: terminated and unresolved conditional options remain visible with the source reason but stay outside allocation. Unweighted options also remain review-only. A tabular XLS/XLSX questionnaire is rejected from the respondent-data path before structural segmentation.
The examples are not templates. File names, sheet names, question codes, and column names may vary. Spreadsheet analysis detects variable semantics and excludes likely respondent identifiers, weights, constants, continuous measures, and free-text answers from segmentation by default. Conditional blanks are excluded from the valid base rather than presented as a segment, while multi-select percentages may legitimately total more than 100%.
Uploaded first-party distributions remain available if public-web research is unavailable. When Additional Websearch is disabled, the grounding pipeline makes no Exa search or content calls, reports “Analyzing files…”, and uses only the attached material. Streaming and settled source rows display the original upload filename rather than the internal upload endpoint. The webapp streams recovered distributions into the draft review, and distribution or segment changes optimistically rebuild the proposed cohort. At creation time, the server recomputes the deterministic allocation and rejects a stale approved preview instead of silently creating a different cohort.
Draft portraits are placeholders, not generated identities. The server matches only rights-approved catalog images to synthetic personas with compatible metadata. Existing public Minds contribute their stored visual-presentation metadata; age and declared representation remain compatibility constraints whenever the source or cohort provides them. The draft response carries a required visual profile, and the server aligns each persona with the best-fitting allocated cohort slot before portrait matching. Exact age and representation matches rank first; generic regional representation is eligible only when the asset has no conflicting specific identity. A compatible portrait may be reused when a narrow demographic bucket contains fewer distinct assets than the requested Audience. If identity metadata conflicts or no compatible portrait exists, the UI renders stable initials rather than a generic face. Hovering any distribution segment filters the orbit by exact cohort assignment, including non-visual axes such as migration history, without pretending those attributes can be inferred from appearance. Named real-person drafts never borrow a stock identity. Manual, Quick, and onboarding creation replace the placeholder as soon as the generated or verified profile image arrives.
V1 and MCP file-grounded creation
POST /api/v1/groups/from-brief and the MCP create_group_from_brief tool use the same classifier, eligibility reconciliation, and deterministic allocation contract as the webapp:
{
"text": "Create the qualified K-12 teacher audience defined by this screener",
"files": [{
"name": "teacher-screener.docx",
"url": "/api/uploads/chat/.../teacher-screener.docx"
}],
"includeWebSearch": false,
"groupCreationMode": "balanced"
}
Set includeWebSearch to false to make the supplied sources authoritative and skip all Exa search and content calls. The response grounding contains:
distributions: only eligible, weighted target or observed segments used for allocation;reviewDistributions: the complete source-identified universe, includingexcluded_by_source, unresolvedconditional, andunweightedsegments with reasons;researchStudyAnalysis: file roles, evidence state, audiences, routing/termination rules, quotas, variable mappings, and warnings;sources: user files under their original file names plus web citations only when web search was enabled.
The same metadata is persisted and returned by GET /api/v1/groups/{id} and MCP get_group, including identified segments that were deliberately not allocated. MCP callers can pass screeners, questionnaires, reports, and respondent exports directly in files; file names and layouts are not templates.
Create a Group from a respondent dataset (Enterprise)
The Benchmark Depth workflow can structurally analyze an XLSX or CSV before creating Minds. Every respondent row contributes to per-variable distributions. Observed joint profiles are counted for audit and plausibility checks, but are not converted one-for-one into Minds.
In the webapp, the initial Create Group screen remains unchanged. After Benchmark analysis finishes, a dedicated review step lets the user remove irrelevant segmentation variables. Minds then rebuilds a broad representative cohort from the remaining distributions.
First preview the dataset:
Endpoint: POST /api/v1/groups/from-brief/segmentation-preview
{
"file": {
"name": "respondents.xlsx",
"url": "/api/uploads/chat/.../respondents.xlsx"
}
}
The endpoint classifies the spreadsheet by content before segmentation. A completed respondent table returns detected variables, likely identifier variables, marginal value distributions, valid and missing base counts, respondent counts, a recommended cohort size, and an internal joint-profile audit. A screener or questionnaire programming grid returns QUESTIONNAIRE_NOT_RESPONDENT_DATA instead of treating answer-definition rows as respondents; pass that file to POST /api/v1/groups/from-brief or MCP create_group_from_brief.
By default, interpretable categorical, ordinal, conditional, multi-select, and derived-segment variables are selected. Likely identifiers, raw weights, constants, high-cardinality continuous measures, and free-text fields are excluded. To change the reviewed dimensions, call the endpoint again with the desired segmentationColumns.
After review, pass the returned distribution preview to grounded group creation:
Endpoint: POST /api/v1/groups/from-brief
{
"text": "Create a respondent panel from the reviewed survey segmentation",
"name": "Election segmentation",
"groupCreationMode": "benchmark_depth",
"datasetSegmentation": {
"fileName": "respondents.xlsx",
"sheetName": "Responses",
"columns": [{
"key": "issue_most",
"label": "Most important issue",
"distribution": [
{ "value": "Cost of living", "respondentCount": 42, "sharePct": 36.84 }
],
"otherRespondentCount": 0
}],
"segmentationColumns": ["issue_most"],
"respondentCount": 114,
"recommendedMindCount": 50
}
}
Use the complete datasetSegmentation object returned by the preview endpoint; the shortened arrays above only show the shape. The server creates a generalizable synthetic cohort whose aggregate attributes approximate the selected distributions. It never copies respondent records or creates one Mind per joint profile. Benchmark Depth uses the workspace's full paid Group allowance: 200 Minds on standard Team, or a custom Enterprise allowance up to the 1,000-Mind system ceiling.
Configure deterministic cohort allocation
Grounded synthetic groups use deterministic allocation by default. The server converts every selected marginal distribution into integer quotas with largest-remainder apportionment, assigns one seeded joint profile to every Mind, appends that assignment to the Mind's persona context, and stores a target-versus-achieved audit. The language model does not decide the proportions.
Public sources often establish marginal percentages without establishing correlations between dimensions. For that reason, the default independent strategy is explicitly an independence-based synthetic reconstruction. Use aligned only when rank-aligned composition is appropriate for your use case; neither strategy turns unsupported correlations into source evidence.
Pass cohortAllocation to POST /api/v1/groups/from-brief:
{
"text": "Affluent residential plot buyers in Guadalajara",
"groupCreationMode": "benchmark_depth",
"cohortAllocation": {
"seed": "guadalajara-benchmark-v1",
"distributionNames": ["Age", "Purchase intent", "Budget"],
"maxDistributions": 20,
"minSegmentCount": 1,
"jointStrategy": "independent",
"targetOverrides": [{
"distribution": "Purchase intent",
"segments": [
{ "label": "High", "pct": 50 },
{ "label": "Medium", "pct": 35 },
{ "label": "Low", "pct": 15 }
]
}],
"includeProfilesInResponse": false
}
}
seedreproduces the same joint assignments for the same inputs. When omitted, the server derives a stable seed from the brief, distributions, and member count.distributionNamesincludes only the named grounded dimensions. Omit it to use all available dimensions.maxDistributionscaps profile width from 1 to 25 without reducing research yield.minSegmentCountgives every positive-target segment a floor from 0 to 20 when the member count makes it feasible.targetOverridesadjusts selected target percentages while preserving originalsourcePctvalues in the audit. Unspecified segments retain their source percentages.jointStrategyacceptsindependentfor seeded independent shuffles oralignedto keep similarly ranked segment blocks together.includeProfilesInResponsereturns all member-level assignments in the creation response. They are always persisted when allocation is enabled.- Assignments always reach the Mind as background persona context and are persisted per member. They are never written into the Mind's user-facing
description. enabledcan be set tofalseto retain grounding without allocating cohort profiles.
To inspect an allocation before creating anything, call:
Endpoint: POST /api/v1/groups/allocation-preview
{
"memberCount": 200,
"seedBasis": "guadalajara-benchmark-v1",
"distributions": [{
"name": "Purchase intent",
"distribution": [
{ "label": "High", "pct": 25, "sourceIdx": 0 },
{ "label": "Other", "pct": 75, "sourceIdx": 0 }
]
}],
"cohortAllocation": {
"seed": "guadalajara-benchmark-v1",
"includeProfilesInResponse": true
}
}
The preview is non-mutating and accepts 1–1,000 members. Actual Group creation still enforces the workspace's paid per-Group allowance. Creation responses include cohortAllocation.config and cohortAllocation.audit; GET /api/v1/groups/{groupId} returns the persisted grounding.allocationConfig, grounding.allocationAudit, and each member's cohortProfile.
Get Group Details
Retrieve details for a specific group, including all its members.
Endpoint: GET /api/v1/groups/{groupId}
Headers:
Authorization: Bearer minds_your_api_key
Response
{
"data": {
"id": "group-123",
"name": "Gen Z Consumers",
"sparkCount": 5,
"sparks": [
{
"id": "spark-1",
"name": "Emma",
"discipline": "College Student",
"profileImageUrl": "https://...",
"createdAt": "2025-10-01T08:00:00.000Z"
},
{
"id": "spark-2",
"name": "Marcus",
"discipline": "Social Media Manager",
"profileImageUrl": "https://...",
"createdAt": "2025-10-05T09:30:00.000Z"
}
],
"createdAt": "2025-12-10T12:00:00.000Z",
"updatedAt": "2025-12-15T14:30:00.000Z",
"isPublic": false,
"currentMemberRole": "owner",
"isSharedWithTeam": false,
"isLinkSharingEnabled": false,
"publicShareId": null
}
}
Example Request
curl -X GET "https://getminds.ai/api/v1/groups/group-123" \
-H "Authorization: Bearer minds_your_api_key"
Error Responses
404 Not Found - Group does not exist or you don't have access
{
"statusCode": 404,
"message": "Group not found"
}
Update Group
Update a group's name. Only the group owner can update the group.
Endpoint: PUT /api/v1/groups/{groupId}
Headers:
Authorization: Bearer minds_your_api_key
Content-Type: application/json
Request Body
{
"name": "Gen Z Early Adopters"
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | New name for the group |
Response
{
"data": {
"id": "group-123",
"name": "Gen Z Early Adopters",
"sparkCount": 5,
"sparks": [
{
"id": "spark-1",
"name": "Emma",
"discipline": "College Student",
"profileImageUrl": "https://..."
}
],
"createdAt": "2025-12-10T12:00:00.000Z",
"updatedAt": "2025-12-16T10:15:00.000Z",
"isPublic": false,
"isSharedWithTeam": false,
"isLinkSharingEnabled": false,
"publicShareId": null,
"currentMemberRole": "owner"
}
}
Example Request
curl -X PUT "https://getminds.ai/api/v1/groups/group-123" \
-H "Authorization: Bearer minds_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "Updated Group Name"}'
Error Responses
400 Bad Request - Missing name
404 Not Found - Group not found or you're not the owner
Delete Group
Delete a group permanently. Only the group owner can delete a group. All group members will be removed, but the sparks themselves are not deleted.
Endpoint: DELETE /api/v1/groups/{groupId}
Headers:
Authorization: Bearer minds_your_api_key
Response
Returns 204 No Content with an empty body on success.
Example Request
curl -X DELETE "https://getminds.ai/api/v1/groups/group-123" \
-H "Authorization: Bearer minds_your_api_key"
Error Responses
404 Not Found - Group not found or you're not the owner
Add Members to Group
Add one or more minds to a group. You can only add minds that you own or have access to.
Endpoint: POST /api/v1/groups/{groupId}/members
Headers:
Authorization: Bearer minds_your_api_key
Content-Type: application/json
Request Body
Single spark:
{
"sparkId": "spark-789"
}
Multiple sparks:
{
"sparkIds": ["spark-789", "spark-101", "spark-202"]
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
sparkId | string | No | Single mind ID to add |
sparkIds | array | No | Array of mind IDs to add |
Note: You must provide either sparkId or sparkIds, but not both.
Response
{
"data": {
"added": 3
}
}
Example Request
curl -X POST "https://getminds.ai/api/v1/groups/group-123/members" \
-H "Authorization: Bearer minds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"sparkIds": ["spark-789", "spark-101"]
}'
Error Responses
400 Bad Request - Missing sparkId/sparkIds or empty array
{
"statusCode": 400,
"message": "sparkId or sparkIds is required"
}
403 Forbidden - Plan member limit reached
{
"statusCode": 403,
"message": "Free plan allows up to 3 Minds per Group. Upgrade for unlimited!",
"data": {
"code": "PLAN_LIMIT",
"limitType": "groupMembers",
"currentPlan": "free",
"limit": 3,
"current": 2,
"requested": 2
}
}
404 Not Found - Group not found, you're not the owner, or one or more sparks not accessible
{
"statusCode": 404,
"message": "Sparks not found: 1f2e3d4c-..."
}
Remove Member from Group
Remove a mind from a group.
Endpoint: DELETE /api/v1/groups/{groupId}/members/{sparkId}
Headers:
Authorization: Bearer minds_your_api_key
Response
Returns 204 No Content with an empty body on success.
Example Request
curl -X DELETE "https://getminds.ai/api/v1/groups/group-123/members/spark-789" \
-H "Authorization: Bearer minds_your_api_key"
Error Responses
404 Not Found - Group not found or you're not the owner
Plan Limits
Different subscription plans have different group limits:
| Plan | Max Groups | Max Members per Group |
|---|---|---|
| Free | 1 | 3 |
| Premium | Unlimited | Unlimited |
| Team | Unlimited | Unlimited |
When you reach your plan limit, you'll receive a 403 error with details about upgrading.
Workflow Example
Here is a complete workflow for creating and managing groups:
# 1. Create a group with initial members
curl -X POST "https://getminds.ai/api/v1/groups" \
-H "Authorization: Bearer minds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Research Group",
"sparkIds": ["spark-1", "spark-2"]
}'
# Response: { "data": { "id": "group-abc", ... } }
# 2. Add more members to the group
curl -X POST "https://getminds.ai/api/v1/groups/group-abc/members" \
-H "Authorization: Bearer minds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"sparkIds": ["spark-3", "spark-4", "spark-5"]
}'
# 3. Get group details to see all members
curl -X GET "https://getminds.ai/api/v1/groups/group-abc" \
-H "Authorization: Bearer minds_your_api_key"
# 4. Update the group name
curl -X PUT "https://getminds.ai/api/v1/groups/group-abc" \
-H "Authorization: Bearer minds_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "UX Research Panel"}'
# 5. Remove a member
curl -X DELETE "https://getminds.ai/api/v1/groups/group-abc/members/spark-3" \
-H "Authorization: Bearer minds_your_api_key"
# 6. Use the group in a panel
curl -X POST "https://getminds.ai/api/v1/panels" \
-H "Authorization: Bearer minds_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Product Feedback Panel",
"groupIds": ["group-abc"]
}'
# 7. List all groups
curl -X GET "https://getminds.ai/api/v1/groups" \
-H "Authorization: Bearer minds_your_api_key"
Error Codes Summary
| Code | Description |
|---|---|
| 400 | Bad Request - Missing required fields or invalid data |
| 401 | Unauthorized - Invalid or missing API key |
| 403 | Forbidden - Plan limit reached or not authorized to modify |
| 404 | Not Found - Group or spark does not exist or not accessible |
| 500 | Internal Server Error - Server-side error |
Next Steps
- Create minds to add to your groups
- Use groups in panels for multi-spark surveys
- Learn about authentication
- Review plan limits and errors