Skip to main content

Users

Base URL: https://api-sls.platzi.com/production-sls-business-domains

License States

StateValueMeaning
FREE0Available for assignment
RESERVED1Assigned to a user but not yet activated
ACTIVE2Currently in use
USED3Was active, now consumed
EXPIRED4Plan-level expiration

Each user in the request has a licenses field:

  • licenses = 0 → No licenses assigned (user invited as INACTIVE)
  • licenses = N (N > 0) → Reserve N licenses

POST /{company_id} - Add/create Users

Activate one or more users in a company. The request is processed asynchronously always.

Rules:

  • Each user must provide exactly one of email, username, or phone_number
  • username and phone_number are special cases. Not enabled by default.
  • Providing multiple identifiers per user is rejected
  • For username or phone_number users, a deterministic fake email is generated: {identifier}+{companyId}0@{companyId}.com
  • If an already existing user is provided, licenses are added to the user, not modified.

Note: To create users with username or phone_number, you must contact your account manager to activate the company_allow_username_phonenumber setting for your company.

Request

POST /v3/users/{company_id}

{
"users": [
{
"email": "user@company.com",
"name": "User Name",
"licenses": 1,
"learning_path_ids": [10, 20],
"group_ids": [5],
"is_admin": false,
"next_url": "/custom/path",
"gender": "female",
"external_id": "EMP-001",
"metadata": {
"document_type": "CC",
"document_number": "1020304050",
"empresa": "Acme",
"tipo_negocio": "Retail",
"regional": "North",
"distrito": "District 1"
}
}
],
"skip_mail_send": false,
"skip_mail_validation": false
}

User Fields

FieldTypeRequiredDescription
emailstringconditionalUser email (max 320 chars, auto-lowercased). Required unless company_allow_username_phonenumber setting is active.
usernamestringconditionalUsername identifier (max 150 chars, auto-lowercased). Only accepted when company setting is active.
phone_numberstringconditionalPhone number with country code, e.g. +573001234567 (max 20 chars, format: +{digits}). Only accepted when company setting is active.
namestringnoUser display name (max 255 chars)
licensesintyesLicenses to assign. 0 = invite without licenses (INACTIVE)
learning_path_idsint[]noLearning paths to assign after activation
group_idsint[]noGroups to assign after activation
is_adminboolnoSet user as company admin (default: false)
genderstringnoUser gender. Accepted values: male, female, no_gender (case-insensitive). Any other value is ignored (not saved) and does not block the request. Saved on the user's Platzi account; only applied when the account is created.
metadataobjectnoAttributes for this user, from the schema defined by Platzi for your company (see User Metadata). Only keys in that schema are accepted; unknown keys are rejected.
external_idstring | numbernoYour own identifier for this user (previous LMS, HR system). Max 128 chars, unique per company (see External Id).

User Metadata

Optional attributes attached to a user. The set of accepted fields is a defined schema managed by Platzi and configured specifically for your company rather than a free-form object: this allowlist safeguards your company's data, keeps values consistent across the platform, and prevents arbitrary or malformed keys from being stored.

Each company has its own set of metadata fields — there is no shared, platform-wide list of keys. The exact fields available to you (their keys and labels) are agreed with Platzi and set up for your company.

Rules:

  • Sending a key outside your company's schema rejects the whole request with a 400.
  • If your company has no metadata schema configured, any metadata sent is rejected. Metadata is opt-in per company: it must be set up first.
  • Keys may be sent using either their identifier or their label.
  • Values are trimmed, empty values are dropped, and each value may be at most 255 characters.

Need metadata fields, or changes to the ones you have? They must be configured for your company by Platzi before they can be sent — new keys are not accepted automatically.

On re-invite, metadata is merged: it is only overwritten when the request supplies it, so a re-invite without metadata keeps the existing values. The stored values are returned by GET /{company_id} under the user_metadata field (the request accepts it as metadata; the response exposes it as user_metadata to avoid clashing with the response-level pagination metadata).

External Id

If your company already identifies its people in another system (a previous LMS, an HR platform, an employee directory), you can attach that identifier to each user when creating them. It is stored on the user's membership in your company and returned in every user listing, so you can reconcile Platzi data against your own records — for example when importing progress history through the Progress Migration API, or when matching reports to your HR system.

Rules:

  • Fully optional. Users created without external_id behave exactly as before; nothing changes for existing integrations.
  • String or number. Both are accepted and normalized to the string form: 9101 and "9101" are the same id. Max 128 characters.
  • Unique per company. Two users in the same company cannot share an external_id. Sending an id already linked to a different user rejects the whole request with EXTERNAL_ID_TAKEN before any user is processed.
  • Idempotent on re-invite. Re-sending the same id for the same user is a no-op. Sending a new id for a user that already has one replaces it. Omitting the field keeps the stored value untouched.
  • Reassignment in one request. You can move an id from one user to another in a single request as long as the current holder receives a different id in the same users array (its old id is released first).
  • The same person can have a different external_id in each company — the id belongs to the company relationship, not to the Platzi account.

The stored value is returned by GET /{company_id} (and by this endpoint's response) under the external_id field.

Identifier Rules

The identifier behavior depends on a company setting. Ask to Platzi organizers to enable them.

Settingemailusernamephone_numberBehavior
InactiverequiredrejectedrejectedStandard email-only invite
Activeone ofone ofone ofExactly one identifier per user

When the setting is active:

  • Each user must provide exactly one of email, username, or phone_number
  • Providing multiple identifiers per user is rejected
  • For username or phone_number users, a deterministic fake email is generated: {identifier}+{companyId}0@{companyId}.com
  • skip_mail_send and skip_mail_validation are automatically forced to true for the entire batch if any user uses username or phone_number

Request Options

FieldTypeDefaultDescription
skip_mail_sendboolfalseSkip sending activation email. Auto-forced to true when batch contains username/phone users.
skip_mail_validationboolfalseSkip ZeroBounce email validation. Auto-forced to true when batch contains username/phone users.

Validation

  • Identifiers must be unique within the request (duplicate emails, usernames, or phone numbers rejected)
  • Email format validated via regex (when provided)
  • Phone number format: + followed by 7-15 digits
  • external_id values must be unique within the request, and must not be linked to a different user in the company

Query Parameters

ParameterTypeDefaultDescription
pageint1Page of the affected users to return
page_sizeint50Results per page (1–100, capped at 100)

Success Response (200)

data is a paginated list of the CompanyUser rows set to processing (same shape as GET /{company_id} — see Response Fields below). Execution bookkeeping sits under metadata.execution; pagination metadata for data sits alongside it.

{
"data": [
{
"company_user_id": 603362,
"user_id": null,
"name": "User Name",
"email": "user@company.com",
"avatar": "https://static.platzi.com/static/website/v2/images/avatar_default.7516253fc982.png",
"start_date": "",
"end_date": "",
"state": "processing",
"error_code": null,
"error_reason": null,
"renew_months": 0,
"months_used": 0,
"total_months": 0,
"role": "student",
"profile_url": null,
"license_extension": false,
"resendable_invitation": false,
"last_invitation_sent_at": null,
"courses": 0,
"start_date_first_month": null,
"user_metadata": null,
"external_id": "EMP-001"
}
],
"metadata": {
"execution": {
"success": true,
"message": "Activation queued for 3 user(s). 3 marked as processing.",
"users_processing": 3,
"execution_arn": "arn:aws:states:us-east-1:123:execution:sm:run-id"
},
"count": 3,
"pages": 1,
"current_page": 1,
"page_size": 50
},
"errors": []
}

Newly-created users appear with user_id: null until the processing job creates their account.

Error Response (400)

{
"data": null,
"errors": [
{
"code": "NOT_ENOUGH_LICENSES",
"message": "Not enough licenses. Required: 5, Available: 2"
}
]
}

Pre-validation Error Codes

CodeDescription
NOT_FLEXIBLE_PLANCompany does not have a flexible plan
PLAN_EXPIREDCompany plan has expired (only when licenses are requested)
NOT_ENOUGH_LICENSESNot enough FREE licenses for the total licenses requested
INVALID_REQUESTCompany not found or malformed request body
EXTERNAL_ID_TAKENAn external_id in the request is already linked to a different user in the company

Sandbox Companies

For sandbox companies (is_sandbox = true), real emails are automatically transformed: user@domain.com becomes user+sandbox@domain.com. This prevents sandbox users from affecting real user accounts. Fake emails (from username/phone invites) are not modified.


PUT /{company_id} - Update Licenses

Update the number of reserved licenses for existing users. The request is processed asynchronously. Unlike invite (POST), licenses is the target count of RESERVED licenses (not additive). The same /{company_id} path is used — the HTTP verb distinguishes the operation: POST to invite, PUT to update.

Request

PUT /v3/users/{company_id}/

{
"users": [
{ "company_user_id": 603362, "licenses": 2 },
{ "company_user_id": 603361, "licenses": 3 }
]
}

User Fields

FieldTypeRequiredDescription
company_user_idintyesExisting CompanyUser ID
licensesintyesTarget number of RESERVED licenses (>= 0)

Per-User Validation

Each user is validated individually before the async process starts. Invalid users are returned in the errors array while valid users proceed to async processing:

ConditionError
company_user_id not found in companyCould not find company user {id} (404)
CompanyUser in error stateCompany user {id} is in error state (400)
CompanyUser in processing stateCompany user {id} is in processing state (400)

Query Parameters

ParameterTypeDefaultDescription
pageint1Page of the affected users to return
page_sizeint50Results per page (1–100, capped at 100)

Hybrid Response (200)

data is a paginated list of the CompanyUser rows set to processing (same shape as GET /{company_id}). Execution bookkeeping sits under metadata.execution; pre-validation per-user errors ride in errors:

{
"data": [
{
"company_user_id": 603362,
"user_id": 12345,
"name": "User Name",
"email": "user@company.com",
"avatar": "https://static.platzi.com/media/avatars/user.jpg",
"start_date": "2025-01-15T00:00:00",
"end_date": "2025-12-31T23:59:59.999999",
"state": "processing",
"error_code": null,
"error_reason": null,
"renew_months": 1,
"months_used": 2,
"total_months": 3,
"role": "student",
"profile_url": "https://platzi.com/@username/",
"license_extension": false,
"resendable_invitation": false,
"last_invitation_sent_at": null,
"courses": 8,
"start_date_first_month": null
}
],
"metadata": {
"execution": {
"success": true,
"message": "Update queued for 3 user(s). 3 marked as processing.",
"users_processing": 3,
"execution_arn": "arn:aws:states:..."
},
"count": 3,
"pages": 1,
"current_page": 1,
"page_size": 50
},
"errors": [
{
"company_user_id": 999,
"message": "Could not find company user 999",
"error_code": 404
},
{
"company_user_id": 888,
"message": "Company user 888 is in error state",
"error_code": 400
}
]
}

If every requested user fails pre-validation, data is [], metadata.execution.users_processing is 0, and metadata.execution.execution_arn is null.

Pre-validation Error Codes

CodeDescription
NOT_FLEXIBLE_PLANCompany does not have a flexible plan
PLAN_EXPIREDCompany plan limit_date < today (only when any licenses > 0)
NOT_ENOUGH_LICENSESNot enough FREE licenses for total increase needed
INVALID_REQUESTInvalid request body or empty users array

DELETE /{company_id} - Delete/Deactivate Users

Delete or deactivate one or more users from a company. The request is processed asynchronously. The delete flag controls the mode: true (default) hard-deletes the user from the company and all associations; false deactivates (sets INACTIVE, releases licenses, preserves associations).

Request

DELETE /v3/users/{company_id}

{
"company_user_ids": [603362, 603361],
"delete": true
}

Fields

FieldTypeRequiredDefaultDescription
company_user_idsint[]yesList of CompanyUser IDs to delete/deactivate
deleteboolnotruetrue = hard delete, false = deactivate

Validation

  • company_user_ids must not be empty
  • company_user_ids must be unique (duplicates rejected with 400)

Per-User Validation

Each user is validated individually before the async process starts. Invalid users are returned in the errors array while valid users proceed to async processing:

ConditionError
company_user_id not found in companyCould not find company user {id} (404)
CompanyUser in processing stateCompany user {id} is already processing (400)
CompanyUser in error state AND delete=falseCannot deactivate company user {id} in error state, use delete=true to remove (400)

Note: Error state users CAN be hard-deleted (delete=true). They cannot be deactivated (delete=false).

Query Parameters

ParameterTypeDefaultDescription
pageint1Page of the affected users to return
page_sizeint50Results per page (1–100, capped at 100)

Hybrid Response (200)

data is a paginated list of the CompanyUser rows set to processing. Execution bookkeeping sits under metadata.execution; pre-validation per-user errors ride in errors:

{
"data": [
{
"company_user_id": 603362,
"user_id": 12345,
"name": "User Name",
"email": "user@company.com",
"avatar": "https://static.platzi.com/media/avatars/user.jpg",
"start_date": "2025-01-15T00:00:00",
"end_date": "2025-12-31T23:59:59.999999",
"state": "processing",
"error_code": null,
"error_reason": null,
"renew_months": 1,
"months_used": 2,
"total_months": 3,
"role": "student",
"profile_url": "https://platzi.com/@username/",
"license_extension": false,
"resendable_invitation": false,
"last_invitation_sent_at": null,
"courses": 8,
"start_date_first_month": null
}
],
"metadata": {
"execution": {
"success": true,
"message": "Delete queued for 2 user(s). 2 marked as processing.",
"users_processing": 2,
"execution_arn": "arn:aws:states:..."
},
"count": 2,
"pages": 1,
"current_page": 1,
"page_size": 50
},
"errors": [
{
"company_user_id": 999,
"message": "Could not find company user 999",
"error_code": 404
}
]
}

If every requested user fails pre-validation, data is [], metadata.execution.users_processing is 0, and metadata.execution.execution_arn is null.

Pre-validation Error Codes

CodeCause
INVALID_REQUESTCompany not found or malformed body

POST /{company_id}/administrators - Add Company Admins

Add one or more users as company administrators. Synchronous endpoint.

Request

POST /v3/users/{company_id}/administrators

{
"company_user_ids": [10, 20, 30]
}

Fields

FieldTypeRequiredDescription
company_user_idsint[]yesList of CompanyUser IDs to promote

Validation

  • company_user_ids must not be empty
  • company_user_ids must be unique (duplicates rejected with 422)

Success Response (200)

{
"data": [
{ "company_user_id": 10, "user_id": 100, "created": true },
{ "company_user_id": 20, "user_id": 200, "created": false }
]
}

created: false means user was already an admin (idempotent).

Partial Failure Response (409)

{
"data": [{ "company_user_id": 10, "user_id": 100, "created": true }],
"errors": [
{
"company_user_id": 999,
"error": "Company user not found or has no linked user",
"error_code": "not_found"
}
]
}

All-Fail Response (404)

{
"data": null,
"errors": [{ "code": "NOT_FOUND", "message": "No valid company users found" }]
}

Error Codes

CodeDescription
not_foundCompanyUser not found or has no linked user_id

DELETE /{company_id}/administrators - Remove Company Admins

Remove one or more users as company administrators. Synchronous endpoint. Also deactivates report subscriptions for the removed admin.

Request

DELETE /v3/users/{company_id}/administrators

{
"company_user_ids": [10, 20, 30]
}

Fields

FieldTypeRequiredDescription
company_user_idsint[]yesList of CompanyUser IDs to demote

Validation

  • company_user_ids must not be empty
  • company_user_ids must be unique (duplicates rejected with 422)

Success Response (200)

{
"data": [{ "company_user_id": 10, "user_id": 100, "removed": true }]
}

Partial Failure Response (409)

{
"data": [{ "company_user_id": 10, "user_id": 100, "removed": true }],
"errors": [
{
"company_user_id": 20,
"error": "User is not an administrator",
"error_code": "not_admin"
},
{
"company_user_id": 999,
"error": "Company user not found or has no linked user",
"error_code": "not_found"
}
]
}

All-Fail Response (404)

{
"data": null,
"errors": [
{
"code": "NOT_FOUND",
"message": "No valid administrators found to remove"
}
]
}

Error Codes

CodeDescription
not_foundCompanyUser not found or has no linked user_id
not_adminUser is not an administrator of this company

Manager Role

A manager administers only the users of the company teams (groups) granted to them, instead of the whole company. Managers can invite, deactivate, assign learning paths and consume company licenses within their groups, and cannot create content, manage administrators or API keys, or delete users permanently.

Requests outside the granted groups are refused with 403 out_of_scope; a manager with no groups assigned gets 403 no_group_scope.

There is no separate endpoint to list managers — use the users listing with ?role=manager (see GET /{company_id}). Each manager row carries a company_teams array with the groups it covers.

POST /{company_id}/managers - Grant Manager Role

Grant the group-scoped manager role to one or more users. Synchronous endpoint.

Request

POST /v3/users/{company_id}/managers

{
"company_user_ids": [10, 20],
"company_team_ids": [301, 302]
}

Fields

FieldTypeRequiredDescription
company_user_idsint[]yesCompanyUser IDs receiving the role (max 100)
company_team_idsint[]yesGroups the manager will cover (1-50)

Validation

  • Both lists must be non-empty and free of duplicates (422 otherwise)
  • company_team_ids must belong to the company and be active
  • A user who is already an administrator is rejected: demote them first

Success Response (200)

{
"data": [
{
"company_user_id": 10,
"user_id": 100,
"created": true,
"company_team_ids": [301, 302]
}
]
}

Error Codes

CodeStatusDescription
INVALID_TEAM400A group is not from this company, or is not active
ALREADY_ADMIN404The user already administers this company
not_found404CompanyUser not found or has no linked user_id

Partial success returns 409 with both data and errors, as in the administrators endpoints.

PUT /{company_id}/managers - Replace Manager Groups

Replace the groups an existing manager covers. Same body and validation as POST. A user without an active grant is reported with not_manager.

Success Response (200)

{
"data": [
{ "company_user_id": 10, "user_id": 100, "company_team_ids": [303] }
]
}

DELETE /{company_id}/managers - Revoke Manager Role

Revoke the role. Deletes the grant and its group links, and deactivates the user's report subscriptions so they stop receiving reports built from the old scope.

Request

DELETE /v3/users/{company_id}/managers

{
"company_user_ids": [10]
}

Success Response (200)

{
"data": [{ "company_user_id": 10, "user_id": 100, "removed": true }]
}

Error Codes

CodeStatusDescription
not_manager404The user holds no manager grant here
not_found404CompanyUser not found or has no linked user_id

GET /{company_id}/managers/me - Own Manager Scope

Returns the caller's own scope. Answers for every role, so a client can call it before deciding what to render.

Success Response (200)

{
"data": {
"is_manager": true,
"company_team_ids": [301, 302],
"company_teams": [{ "id": 301, "name": "North Office", "color": "#112233" }]
}
}

An administrator or organizer gets {"is_manager": false, "company_teams": []}.


GET /{company_id} - List Company Users

Returns a paginated, filterable list of company users. Read-only synchronous endpoint.

Request

GET /v3/users/{company_id}?page=1&page_size=50&state=active&search=john&ordering=-name

Query Parameters

ParameterTypeDefaultDescription
pageint1Page number (>= 1)
page_sizeint50Results per page (1–100, capped at 100)
statestringFilter by state: matriculate, active, inactive, error, processing
searchstringCase-insensitive search on name and email
rolestring'admin' or 'student'
teamstringFilter by team name (active teams only)
learning_path_idintFilter by active enrollment in a learning path
include_groupsstringfalse'true' attaches each row's groups (see Response Fields)
license_extensionstring'true'/'false' — pending license extension
resendable_invitationstring'true'/'false' — eligible for resend (10-day cooldown)
company_user_idstringComma-separated CompanyUser IDs to include
not_company_user_idstringComma-separated CompanyUser IDs to exclude
user_idstringComma-separated auth user IDs to include
orderingstring-idMulti-field ordering, prefix - for DESC

Ordering Fields

Supported values for the ordering parameter. Prefix with - for descending order.

name, email, state, end_date, renew_months, months_used, months_active, resendable_invitation, courses, id (default)

Response (200)

{
"data": [
{
"company_user_id": 603362,
"user_id": 12345,
"name": "John Doe",
"email": "john@company.com",
"avatar": "https://static.platzi.com/media/avatars/john.jpg",
"start_date": "2025-01-15T00:00:00",
"end_date": "2025-12-31T23:59:59.999999",
"state": "active",
"error_code": null,
"error_reason": null,
"renew_months": 2,
"months_used": 5,
"total_months": 6,
"role": "admin",
"profile_url": "https://platzi.com/@johndoe/",
"license_extension": false,
"resendable_invitation": false,
"last_invitation_sent_at": null,
"courses": 15,
"start_date_first_month": "2025-01-15T00:00:00",
"user_metadata": {
"document_type": "CC",
"document_number": "1020304050",
"regional": "North"
},
"external_id": "EMP-001"
}
],
"metadata": {
"count": 150,
"pages": 8,
"current_page": 1,
"page_size": 20
}
}

Response Fields

FieldDescription
nameDisplay name. Shows 'Nuevo Estudiante' if not set
emailUser's email address
avatarAvatar URL or default avatar
start_dateStart of current activity window
end_dateEnd of current activity window (extended by reserved months)
error_codeRaw error code (string) or null when there is no error (see Error Code Mapping below)
error_reasonHuman-readable explanation of error_code, or null when there is no error
renew_monthsNumber of RESERVED licenses
months_usedNumber of ACTIVE + USED licenses
total_monthsrenew_months + months_active + months_used
role'admin' or 'student'
profile_urlPlatzi profile URL or null
license_extensionTrue if user has a pending license extension request and is active or inactive
resendable_invitationTrue if eligible for invitation resend (10-day cooldown)
start_date_first_monthStart date of first license (active/inactive states only)
user_metadataCustom attributes for the user, or null when none were set. Same keys as the request metadata (see User Metadata)
external_idYour own identifier for this user, or null when none was set (see External Id)
groupsGroups this user belongs to, as [{id, name, color}]. Only present when include_groups=true, and then on every row — [] means the user belongs to no group. Distinct from company_teams, which appears on manager rows and names the groups they administer

Group Membership

The listing does not carry groups by default. Ask for it explicitly:

GET /v3/users/{company_id}?include_groups=true&page=1&page_size=50

Each row then gains a groups array:

{
"company_user_id": 603362,
"user_id": 12345,
"name": "John Doe",
"groups": [{ "id": 36046, "name": "Backend", "color": "#0af" }]
}

Only this company's active groups are listed. Rows whose user_id is still null — invitations nobody has accepted yet — always come back with "groups": []: a user has to exist before they can belong to a group.

groups never reaches past what the caller may already read. For a manager limited to certain groups, a user's row lists only those — not every group that user happens to belong to.

Use this instead of calling GET /v3/group-builder/{company_id}/groups/{group_id}/users/ once per group and matching the members back to your users — one request answers for the whole page.

Error Code Mapping

error_code is the raw value; error_reason is the matching text below. Any code not listed here yields error_reason: null.

error_codeerror_reason
6student has B2C paid subscription
7invalid email
11server error at processing
13multiple profiles found at activation
15B2B account already exists

POST /{company_id}/bulk - Bulk Add or Remove License Months

Modify reserved license months for many users matching filters. Async operation (Step Function).

Scope: activations.write
Plan: flexible only

Provide exactly one of:

FieldDescription
licenses_to_addAdd N reserved months to each matched user
licenses_to_removeRemove N reserved months from each matched user
access_untilTrim reserved months so projected access ends on this date (YYYY-MM-DD)

Example — remove 2 reserved months

POST /v3/users/{company_id}/bulk
{
"licenses_to_remove": 2,
"company_user_ids_include": [101, 102, 103]
}

Example — trim all users to a target access date

Releases enough reserved months per user so projected access does not extend past the date. Users whose active window already ends after the date, or who are already compliant, are reported in errors with code 422 or 409.

POST /v3/users/{company_id}/bulk
{
"access_until": "2026-09-30",
"state": "active"
}

Per-user outcomes:

error_codeMeaning
409User already ends on or before access_until (skipped)
422access_until is before active coverage; only reserved months can be trimmed
422No coverage to trim
400User in error / processing, or insufficient reserved months for fixed licenses_to_remove

Example — add months with filters

POST /v3/users/{company_id}/bulk
{
"licenses_to_add": 1,
"state": "active",
"team": "Engineering"
}

Users with fewer reserved months than licenses_to_remove are skipped and returned in errors.


POST /{company_id}/extensions/accept-all - Accept All Pending Extensions

Grant one reserved month to every user with a pending license extension request in the company (same as clicking "Accept" in the licenses table). Async bulk modify under the hood.

Scope: activations.write
Plan: flexible only

POST /v3/users/{company_id}/extensions/accept-all
{}

Optional filters:

{
"company_user_ids_include": [101, 102],
"company_user_ids_exclude": [103]
}

Response shape matches POST /bulk: data (affected users), metadata.execution, and per-user errors when some requests cannot be queued (not enough company licenses, user in error state, etc.).


GET /{company_id}/metadata-fields - List Company Metadata Fields

Returns the metadata schema a company accepts for its users. Read-only synchronous endpoint.

Use it to discover which extra columns can accompany a user (e.g. before building an invite_users bulk file). An empty fields list means the company accepts no metadata.

Request

GET /v3/users/{company_id}/metadata-fields

Path Parameters

NameTypeDescription
company_idintCompany identifier

Success Response

{
"data": {
"fields": [
{ "key": "area", "label": "Área", "required": false },
{ "key": "sede", "label": "Sede", "required": true }
]
},
"errors": []
}
FieldTypeDescription
keystringStable identifier for the metadata field.
labelstringHuman-readable name (accepted as a column-header alias).
requiredbooleanWhether the field must be provided when metadata is set.

See API Key Scopes.