Groups
Base URL: https://api-sls.platzi.com/production-sls-business-domains
Key Features:
- CRUD operations for company teams/groups
- User-to-group assignment and management
- Group-based learning path management
- State management (active, archived, deleted)
- Bulk user operations with partial success handling
- Pagination and search capabilities
API Endpoints
GET /v3/group-builder/{company_id}/groups/
List all groups for a company with optional filters.
Path Parameters:
company_id(int) - Company ID
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| state | string | No | Filter by state: active, archived, deleted (default: active) |
| group_ids | string | No | Comma-separated group IDs to filter |
| search | string | No | Search by group name |
| page | int | No | Page number (default: 1) |
| page_size | int | No | Items per page (default: 20) |
Example Request:
GET /v3/group-builder/123/groups/?state=active&search=engineering&page=1&page_size=20
Response:
{
"data": [
{
"id": 456,
"group_id": 456,
"name": "Engineering Team",
"description": "Software engineers and developers",
"color": "#3498db",
"state": 0,
"students_count": 25,
"created_at": "2025-01-20T10:00:00",
"updated_at": "2025-01-20T10:00:00"
},
{
"id": 457,
"group_id": 457,
"name": "Engineering - Backend",
"description": "Backend developers",
"color": "#2ecc71",
"state": 0,
"students_count": 12,
"created_at": "2025-01-21T09:00:00",
"updated_at": "2025-01-21T09:00:00"
}
],
"metadata": {
"count": 45,
"pages": 3,
"current_page": 1,
"page_size": 20
}
}
id and group_id are duplicate fields with the same value (kept for backwards compatibility). state is an integer — see Group States for the mapping.
POST /v3/group-builder/{company_id}/groups/
Create a new group for a company.
Path Parameters:
company_id(int) - Company ID
Request Body:
{
"name": "Engineering Team",
"description": "Software engineers and developers",
"color": "#3498db"
}
Validation Rules:
name(required): Max 250 characters, must be unique per companydescription(optional): Text fieldcolor(optional): Hex color code (e.g., #3498db)
Response (201 Created):
{
"data": {
"id": 456,
"group_id": 456,
"name": "Engineering Team",
"description": "Software engineers and developers",
"color": "#3498db",
"state": 0,
"students_count": 0,
"created_at": "2025-01-20T10:00:00",
"updated_at": "2025-01-20T10:00:00"
}
}
PUT /v3/group-builder/{company_id}/groups/{group_id}/
Update an existing group's details.
Path Parameters:
company_id(int) - Company IDgroup_id(int) - Group ID
Request Body (all fields optional):
{
"name": "Engineering Team - Updated",
"description": "All software engineers",
"color": "#2980b9",
"state": "active"
}
State Options:
active- Group is active and visiblearchived- Group is archived but not deleteddeleted- Group is deleted
Response:
{
"data": {
"id": 456,
"group_id": 456,
"name": "Engineering Team - Updated",
"description": "All software engineers",
"color": "#2980b9",
"state": 0,
"students_count": 25,
"created_at": "2025-01-20T10:00:00",
"updated_at": "2025-01-20T15:30:00"
}
}
DELETE /v3/group-builder/{company_id}/groups/{group_id}/
Delete a group.
Path Parameters:
company_id(int) - Company IDgroup_id(int) - Group ID
Behavior:
- Soft-deletes the group: marks state as deleted and timestamps the name (
"<original_name>-YYYYMMDDHHMMSS") so the original name can be reused.
Response:
{
"data": {
"id": 456,
"group_id": 456,
"name": "Engineering Team-20250120103000",
"description": "Software engineers and developers",
"color": "#3498db",
"state": 2,
"students_count": 25,
"created_at": "2025-01-20T10:00:00",
"updated_at": "2025-01-20T10:30:00"
}
}
GET /v3/group-builder/{company_id}/groups/{group_id}/users/
List all users in a group.
Path Parameters:
company_id(int) - Company IDgroup_id(int) - Group ID
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| page | int | No | Page number (default: 1) |
| page_size | int | No | Items per page (default: 20) |
Example Request:
GET /v3/group-builder/123/groups/456/users/?page=1&page_size=20
Response:
{
"data": [
{
"usergroup_id": 12345,
"company_user_id": 789,
"name": "John Doe",
"email": "john.doe@company.com",
"state": "active",
"end_date": "2025-12-31",
"avatar_url": "https://static.platzi.com/media/avatars/john.jpg"
},
{
"usergroup_id": 12346,
"company_user_id": 790,
"name": "Jane Smith",
"email": "jane.smith@company.com",
"state": "active",
"end_date": "2025-12-31",
"avatar_url": "https://static.platzi.com/static/website/v2/images/avatar_default.7516253fc982.png"
}
],
"metadata": {
"count": 25,
"pages": 2,
"current_page": 1,
"page_size": 20
}
}
Response Fields:
| Field | Type | Description |
|---|---|---|
usergroup_id | int | ID of the user-group relationship row |
company_user_id | int | Company user ID |
name | string | null | User display name |
email | string | User email |
state | string | Company-user state (e.g. active, inactive, processing) |
end_date | string | null | End of the user's current activity window (YYYY-MM-DD) |
avatar_url | string | Avatar URL (falls back to the Platzi default avatar) |
POST /v3/group-builder/{company_id}/groups/{group_id}/users/
Assign users to a group (bulk operation with partial success support).
Path Parameters:
company_id(int) - Company IDgroup_id(int) - Group ID
Request Body:
{
"company_user_ids": [789, 790, 791, 792]
}
Behavior:
- Attempts to assign all users
- Returns successful assignments even if some fail
- Reports errors for failed assignments
- Skips users already in group
Response (200 OK - All successful):
{
"data": [
{ "company_user_id": 789, "usergroup_id": 12345 },
{ "company_user_id": 790, "usergroup_id": 12346 }
]
}
usergroup_id is the ID of the user-group relationship row (same value as in GET /users/).
Response (409 Conflict - Partial success):
{
"data": [
{ "company_user_id": 789, "usergroup_id": 12345 }
],
"errors": [
{
"company_user_id": 791,
"message": "User not found",
"error_code": "user_not_found"
},
{
"company_user_id": 792,
"message": "User has no linked account",
"error_code": "user_not_linked"
}
]
}
Response (400 Bad Request - All failed):
{
"errors": [
{
"company_user_id": 789,
"message": "User not found",
"error_code": "user_not_found"
},
{
"company_user_id": 790,
"message": "User not found",
"error_code": "user_not_found"
}
]
}
DELETE /v3/group-builder/{company_id}/groups/{group_id}/users/
Remove users from a group (bulk operation with partial success support).
Path Parameters:
company_id(int) - Company IDgroup_id(int) - Group ID
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| company_user_ids | string | Yes | Comma-separated list of user IDs |
Example Request:
DELETE /v3/group-builder/123/groups/456/users/?company_user_ids=789,790,791
Response (200 OK - All successful):
{
"data": [789, 790]
}
data is a list of the company_user_ids that were removed.
Response (409 Conflict - Partial success):
{
"data": [789],
"errors": [
"Company user 790 not found",
"User 791 was not in the group"
]
}
errors is a list of plain strings for this endpoint (not error objects).
Response (400 Bad Request - All failed):
{
"errors": [
"Company user 789 not found",
"Company user 790 not found"
]
}
If the company_user_ids query parameter is not a valid comma-separated integer list, the API returns 400 with the standard error envelope:
{
"errors": [
{ "message": "Invalid company_user_ids", "error_code": "bad_request" }
]
}
GET /v3/group-builder/{company_id}/groups/{group_id}/learning-paths/
List all learning paths assigned to a group.
Path Parameters:
company_id(int) - Company IDgroup_id(int) - Group ID
Example Request:
GET /v3/group-builder/123/groups/456/learning-paths/
Response:
{
"data": [
{
"id": 101,
"title": "Backend Development with Python",
"slug": "backend-python",
"badge": "achievements/badge-backend-python.png",
"state": 1,
"created_by": 0,
"private": true
},
{
"id": 102,
"title": "DevOps Fundamentals",
"slug": "devops-fundamentals",
"badge": "achievements/badge-devops.png",
"state": 1,
"created_by": 0,
"private": false
}
]
}
Response Fields:
| Field | Type | Description |
|---|---|---|
id | int | Learning path ID |
title | string | Learning path title |
slug | string | URL slug |
badge | string | null | Relative badge path (prefix with https://static.platzi.com/media/ to get a full URL) |
state | int | Catalog state (only 1 is returned by this endpoint; see mapping below) |
created_by | int | Source of the catalog entry (see mapping below) |
private | boolean | Whether the learning path is private to a company |
State Values:
| Value | Meaning |
|---|---|
0 | draft |
1 | active |
2 | archived |
3 | deleted |
Created By Values:
| Value | Meaning |
|---|---|
0 | admin (created by a Platzi admin) |
1 | placement_test |
2 | skill_matcher |
Group States
state is returned as an integer in responses. Use the state query parameter on the list endpoint with the name values (active, archived, deleted).
State Definitions
| Name | Integer Value | Description | Visible in List Default |
|---|---|---|---|
| active | 0 | Group is active and visible | Yes |
| archived | 1 | Group is archived but not deleted | No (use state=archived) |
| deleted | 2 | Group is soft-deleted | No (use state=deleted) |
State Behavior
ACTIVE:
- Default state for new groups
- Visible in default listings
- Users can be assigned
- Learning paths can be assigned
ARCHIVED:
- Group is archived but still accessible
- Not visible in default listings (requires state filter)
- Existing users remain in group
- Can be reactivated to ACTIVE
DELETED:
- Soft-deleted (the group is preserved for historical reporting)
- Name is timestamped to allow reuse
- Not visible in default listings
- Users remain associated (for historical data)
- Cannot be reactivated (create a new group instead)
Bulk Operations with Partial Success
HTTP Status Codes
| Scenario | Status Code | Response Structure |
|---|---|---|
| All successful | 200 OK | { data: [...] } |
| Partial success | 409 Conflict | { data: [...], errors: [...] } |
| All failed | 400 Bad Request | { errors: [...] } |
The shape of errors[] differs between assign (objects: {company_user_id, message, error_code}) and remove (plain strings) — see each endpoint's example.
Error Handling
Common Error Scenarios
| Error | Status Code | Message | Resolution |
|---|---|---|---|
| Group not found | 404 | Group id {id} not found in company id {company_id} | Verify group exists and belongs to company |
| Duplicate group name | 400 | Group with name '{name}' already exists | Use a different name |
| Invalid state value | 400 | Invalid state: {state} | Use active, archived, or deleted |
| User not in company | 400 | User {id} does not belong to company {company_id} | Verify user-company relationship |
| User already in group | 409 | User {id} is already in group {group_id} | Partial success response |
| User not in group | 409 | User {id} not found in group {group_id} | Partial success response |
| Validation error | 400 | Invalid request parameters | Check request body format |
Best Practices
Group Naming
Recommended:
- Descriptive names: "Engineering Team", "Sales - North Region"
- Consistent naming convention across company
- Avoid special characters that may cause issues
Avoid:
- Generic names: "Group 1", "Team A"
- Very long names (> 100 characters)
- Names with only special characters
State Management
Active Groups:
- Use for current, operational teams
- Default state for new groups
- Visible in all listings
Archived Groups:
- Use for temporary or project-based teams that are complete
- Preserves historical data
- Can be reactivated if needed
Deleted Groups:
- Use for groups that should not be reactivated
- Name is timestamped for historical tracking
- Consider archiving instead of deleting when possible
Bulk Operations
Best Practices:
- Limit batch size to 50 users per request
- Handle partial success responses gracefully
- Retry failed operations individually
- Log all errors for debugging
Example Error Handling:
response = assign_users_to_group(company_id, group_id, [1, 2, 3, 4, 5])
if response.get('errors'):
# Partial success - some users failed
logger.warning(f"Partial success: {len(response['errors'])} errors")
for error in response['errors']:
logger.error(error)
# Optionally: retry failed users individually
if response.get('data'):
# Log successful assignments
logger.info(f"Successfully assigned {len(response['data'])} users")
Troubleshooting
Groups Not Appearing in List
Symptoms: Groups exist but don't appear in listings
Diagnosis:
- Check group state (may be archived or deleted)
- Verify company_id in query
- Check pagination parameters
Resolution:
- Use
stateparameter to include archived/deleted groups - Verify correct company_id
- Increase page_size or check other pages
Duplicate Name Error
Symptoms: Cannot create group with existing name
Diagnosis:
- Check if name already exists in company
- Check if deleted group has same name (without timestamp)
Resolution:
- Use a different name
- Deleted groups have timestamped names, so the original name should be available
- If the error persists, contact support to confirm whether a duplicate name exists
Bulk Assignment Failures
Symptoms: All or most users fail to assign
Diagnosis:
- Check user IDs belong to correct company
- Verify group exists and is active
- Check if users are already in group
Resolution:
- Validate all user IDs before batch operation
- Use list_group_users to check current members
- Remove already-assigned users from batch
Slow List Operations
Symptoms: Group listing takes > 1 second
Resolution:
- Use pagination with smaller
page_size - Add
searchto narrow results - Cache frequently accessed responses on the client side
- If the problem persists, contact support