> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gigstack.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Users API Guide

> Integration guide for Users

<Warning>Some operations in this guide have Discovery handlers but no public gateway route yet: `POST /users/reset-password/{id}`. Check the availability notice on each API reference page before using them.</Warning>

Manage user accounts, profiles, and access control. The Users API handles user creation, updates, password management, and team associations.

## Overview

Users represent individual accounts that can access teams and resources. Each user has a profile, role assignments, and can belong to multiple teams with different permission levels.

## Key Features

* **User Management** - Create and update user accounts
* **Profile Information** - Manage user details and contact info
* **Password Reset** - Secure password management
* **Login Link Generation** - Create direct login links for API-created users
* **Role Assignment** - Control access levels per team
* **Multi-team Support** - Users can belong to multiple teams
* **Address Management** - Store user location information

## Endpoints

### List Users

```http theme={null}
GET /users
```

Retrieve a paginated list of users.

**Query Parameters:**

* `limit` (integer, 1-100) - Number of results per page (default: 10)
* `next` (string) - Pagination cursor for next page
* `team` (string) - gigstack Connect: Target team ID

**Example Request:**

```bash theme={null}
curl -X GET "https://api.gigstack.io/v2/users?limit=20" \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Example Response:**

```json theme={null}
{
    "message": "Users retrieved successfully",
    "data": [
        {
            "id": "user_1234567890",
            "email": "john.doe@example.com",
            "created_at": 1677651234000,
            "first_name": "John",
            "last_name": "Doe"
        },
        {
            "id": "user_9876543210",
            "email": "jane.smith@example.com",
            "created_at": 1677651234000,
            "first_name": "Jane",
            "last_name": "Smith"
        }
    ],
    "has_more": false,
    "total_results": 2
}
```

### Create User

```http theme={null}
POST /users
```

Create a new user account. Creation does not send an invitation email and does not
return the generated password. Choose the documented login-link or password-reset
flow separately; check availability before relying on a reset route.

**Current phone limitation:** omit `phone` when creating an account, even if you
have a correctly formatted international number. The reviewed creation path sends
it to the authentication provider in an unsupported format. Changing the format in
your request does not solve this. The examples below omit it; this limitation is
source-reviewed and has not been exercised against a live account.

**Request Body:**

```json theme={null}
{
    "email": "new.user@example.com",
    "first_name": "New",
    "last_name": "User",
    "company_role": "Developer",
    "address": {
        "country": "MEX",
        "street": "Av. Reforma",
        "zip": "06500",
        "city": "Ciudad de México",
        "state": "CDMX",
        "exterior": "456",
        "neighborhood": "Juárez"
    },
    "auto_join": true,
    "role": "editor"
}
```

**Body Parameters:**

* `email` (string, required for creation) - Supply a valid, unique email address. The shared schema marks it optional, but account creation uses it. It is not writable through `PUT /users/{id}`.
* `first_name` (string, optional) - User first name
* `last_name` (string, optional) - User last name
* `phone` (string, optional) - Omit on creation: the current account-creation path rejects populated phone numbers. Profile contact-phone updates are a separate operation; read its membership caveat before using it.
* `company_role` (string, optional) - User role in the company
* `address` (object, optional) - User address information
  * `country` (string, optional) - Country name
  * `street` (string, optional) - Street address
  * `zip` (string, optional) - Postal code
  * `city` (string, optional) - City name
  * `state` (string, optional) - State/province
  * `exterior` (string, optional) - Exterior number
  * `municipality` (string, optional) - Municipality (stored but not returned in responses)
  * `neighborhood` (string, optional) - Neighborhood/colony
* `auto_join` (boolean, optional) - Adds the new user to the effective team when true. Defaults to false for the credential's own team. A Connect call targeting another eligible team joins that team automatically, even if this flag is false.
* `role` (string, optional) - Role to assign to the user when `auto_join` is `true`. Can be `"editor"`, `"admin"`, or `"viewer"`. Defaults to `"viewer"` if not specified. It applies whenever creation joins the user to a team, either through `auto_join` or a Connect target.

**Notes:**

* The `municipality` field can be included in requests and will be stored, but is not returned in response objects.
* When creation joins the user, it adds the effective team and billing account to the user record. Connect uses the selected team, not the master team.
* The `role` parameter works in conjunction with `auto_join`. When both are used, the user is added to the team with the specified role (editor, admin, or viewer). If `role` is not specified, the user defaults to "viewer" role.

**Example Request:**

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/users \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "developer@company.com",
    "first_name": "Carlos",
    "last_name": "Rodriguez",
    "company_role": "Senior Developer",
    "auto_join": true,
    "role": "editor"
  }'
```

**Example Response:**

```json theme={null}
{
    "message": "User created",
    "data": {
        "id": "user_new123456",
        "email": "developer@company.com",
        "created_at": 1677651234000,
        "first_name": "Carlos",
        "last_name": "Rodriguez"
    },
    "success": true
}
```

### Get User

```http theme={null}
GET /users/{id}
```

Retrieve a specific user by ID.

**Example Request:**

```bash theme={null}
curl -X GET https://api.gigstack.io/v2/users/user_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Example Response:**

```json theme={null}
{
    "message": "User retrieved successfully",
    "data": {
        "id": "user_1234567890",
        "email": "john.doe@example.com",
        "first_name": "John",
        "last_name": "Doe",
        "phone": "+52 55 1234 5678",
        "company_role": "Manager",
        "address": {
            "country": "MEX",
            "street": "Av. Insurgentes Sur",
            "zip": "03100",
            "city": "Ciudad de M\u00e9xico",
            "state": "CDMX",
            "exterior": "123",
            "neighborhood": "Del Valle"
        },
        "created_at": 1677651234000,
        "teams": [
            "team_1234567890",
            "team_0987654321"
        ]
    }
}
```

### Update User

```http theme={null}
PUT /users/{id}
```

Update an existing user's information.

**Request Body:**

The profile fields are optional. `email` and membership fields are reserved and
removed from updates; this operation cannot change the authentication email or assign
a team role. Use the Teams API for membership changes.

**Current implementation caveat:** the update mapper defaults the user document's
`teams` array to `[]` and merges it into the record, even when you omit `teams`.
Sending that reserved field does not preserve it. This is a source-observed limitation,
not a live-tested guarantee about access: team-side membership is separate. Avoid
using this endpoint as a harmless profile-only write until you have checked its effect
on your integration; read the user and team membership back and contact support if
those records disagree.

```json theme={null}
{
    "first_name": "Jonathan",
    "last_name": "Doe",
    "phone": "+52 55 5555 5555",
    "company_role": "Senior Manager",
    "address": {
        "zip": "03200",
        "neighborhood": "Del Valle Sur"
    }
}
```

**Example Request:**

```bash theme={null}
curl -X PUT https://api.gigstack.io/v2/users/user_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+52 55 9999 9999",
    "company_role": "Director"
  }'
```

### Reset Password

```http theme={null}
POST /users/reset-password/{id}
```

Sends a password-reset email to the user. **The user is identified by the path parameter — there is no request body**, and passing an `email` in the body has no effect. The address used is the one on the user's auth record, not anything you supply.

The email template describes a 24-hour expiration, but this handler does not set
the authentication provider's expiration policy. Follow the link's actual result and
request a new reset if expired; do not use that template text as a session guarantee.

**Example Request:**

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/users/reset-password/user_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN"
```

**Example Response:**

```json theme={null}
{
    "message": "User password reset successfully"
}
```

### Generate Login Link

```http theme={null}
POST /users/login-link
```

Generate a login link for a user. The link contains a custom Firebase token that allows the user to authenticate directly without a password.

**Requirements:**

* User must have been created via API (from: 'api')
* The API-created user must be managed by, or attached to, the caller's billing account
* The user must belong to or own at least one team, and every such team must belong to that billing account; membership in an outside tenant is refused
* If requirements are not met, returns a 404 error

**gigstack Connect:** Generate login links for other teams' users using the `team` query parameter.

**Request Body:**

```json theme={null}
{
    "user_id": "abc123xyz"
}
```

**Parameters:**

* `user_id` (required, string) - The Firebase UID of the user

**Example Request:**

```bash theme={null}
curl -X POST https://api.gigstack.io/v2/users/login-link \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "abc123xyz"
  }'
```

**Example Response:**

```json theme={null}
{
    "message": "Login link generated",
    "data": {
        "login_link": "https://app.gigstack.pro/token-login?token=TOKEN_PLACEHOLDER",
        "valid_until": 1732657200000,
        "method": "token"
    }
}
```

**Response Fields:**

* `login_link` (string) - The login URL with embedded token
* `valid_until` (number) - Unix timestamp in milliseconds when the token expires (1 hour from generation)
* `method` (string) - The authentication method used (always 'token')

**Error Responses:**

**404 - User Not Found or Not Accessible:**

```json theme={null}
{
    "message": "User not found or not accessible via API",
    "error": "The user either does not exist or was not created via API"
}
```

## User Structure

### User Fields

| Field | Type | Description |
| - | - | - |
| `id` | string | Unique user identifier |
| `email` | string | User's email address (unique) |
| `first_name` | string | User's first name |
| `last_name` | string | User's last name |
| `phone` | string | Contact phone number |
| `company_role` | string | Position in company |
| `teams` | string\[] | Team IDs recorded on the user; retrieve team membership for roles |
| `address` | object | User's address information |
| `created_at` | number | Unix timestamp of creation in milliseconds |

### Address Structure

The address object supports the following fields:

```json theme={null}
{
    "country": "MEX",
    "street": "Street name",
    "zip": "12345",
    "city": "City",
    "state": "State",
    "exterior": "123",
    "neighborhood": "Neighborhood"
}
```

**Note:**

* The `municipality` field can be included in POST/PUT requests and will be stored in the database, but is not returned in GET responses.
* All address fields are optional and nullable.
* Address fields returned in responses: `country`, `street`, `zip`, `city`, `state`, `exterior`, `neighborhood`

## User Roles and Permissions

### System Roles

Roles belong to a **team membership**, not a global `role` field on the user response.
Use `admin`, `editor` or `viewer` when adding a member. `member` is not an accepted
role for the Teams API.

### Role Hierarchy

`admin` can administer the team; `editor` and `viewer` describe progressively narrower
resource permissions. Individual module permissions can also affect user-scoped access.

### Permission Matrix

The required role depends on the operation and credential. User-scoped MCP/dashboard
credentials are checked against the relevant team and module permissions. API keys
and OAuth access tokens represent the team; they do not inherit the current personal
role of whoever originally created them. OAuth tokens cannot switch teams through
Connect. See [credential scope](/guides/gigstack-connect#permission-requirements) and the
operation's reference before granting access or generating a user's login link.

## Common Scenarios

### 1. Onboard New Employee

**Option A: Using auto\_join with role (Recommended)**

```bash theme={null}
# Create user and automatically add to team with editor role
curl -X POST https://api.gigstack.io/v2/users \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newemployee@company.com",
    "first_name": "Maria",
    "last_name": "Garcia",
    "company_role": "Accountant",
    "auto_join": true,
    "role": "editor"
  }'
```

**Option B: Manual team addition**

```bash theme={null}
# Create user
curl -X POST https://api.gigstack.io/v2/users \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newemployee@company.com",
    "first_name": "Maria",
    "last_name": "Garcia",
    "company_role": "Accountant"
  }'

# Add to team (using Teams API)
curl -X POST https://api.gigstack.io/v2/teams/team_123/add-member \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "user_new123",
    "role": "editor"
  }'
```

### 2. Update User Profile

```bash theme={null}
curl -X PUT https://api.gigstack.io/v2/users/user_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Maria",
    "last_name": "Garcia Lopez",
    "phone": "+52 55 3333 4444",
    "company_role": "Senior Accountant",
    "address": {
      "country": "MEX",
      "city": "Guadalajara",
      "state": "Jalisco",
      "zip": "44100"
    }
  }'
```

### 3. Bulk User Creation with Different Roles

```javascript theme={null}
// Example: Create multiple users with different roles
const users = [
    {
        email: 'admin@company.com',
        first_name: 'Admin',
        last_name: 'User',
        company_role: 'Manager',
        auto_join: true,
        role: 'admin',
    },
    {
        email: 'developer@company.com',
        first_name: 'Developer',
        last_name: 'User',
        company_role: 'Developer',
        auto_join: true,
        role: 'editor',
    },
    {
        email: 'auditor@company.com',
        first_name: 'Auditor',
        last_name: 'User',
        company_role: 'External Auditor',
        auto_join: true,
        role: 'viewer',
    },
]

for (const user of users) {
    await fetch('https://api.gigstack.io/v2/users', {
        method: 'POST',
        headers: {
            Authorization: 'Bearer YOUR_TOKEN',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(user),
    })
}
```

### 4. Password Reset Flow

```bash theme={null}
# Request password reset (user id in the path, no body)
curl -X POST https://api.gigstack.io/v2/users/reset-password/user_1234567890 \
  -H "Authorization: Bearer YOUR_TOKEN"

# If this operation is publicly available and succeeds, the user receives the reset email
# User clicks link and sets new password (handled by web app)
```

### 5. Generate Login Link for API User

```bash theme={null}
# Create user via API
USER_RESPONSE=$(curl -X POST https://api.gigstack.io/v2/users \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "apiuser@example.com",
    "first_name": "API",
    "last_name": "User",
    "company_role": "External User"
  }')

# Extract user ID from response
USER_ID=$(echo $USER_RESPONSE | jq -r '.data.id')

# Generate login link
curl -X POST https://api.gigstack.io/v2/users/login-link \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"user_id\": \"$USER_ID\"
  }"

# Response includes login link valid for 1 hour
# Send this link to the user via email, SMS, etc.
```

**Use Cases:**

* Onboard external users without requiring password setup
* Provide temporary access to contractors or auditors
* Enable single sign-on for integrated applications
* Create magic links for embedded user experiences

## User Lifecycle

### User Creation Flow

```mermaid theme={null}
graph LR
    A[Create User] --> B[Send Invitation]
    B --> C[User Accepts]
    C --> D[Set Password]
    D --> E[First Login]
    E --> F[Active User]
```

### User Deactivation Flow

This is a workflow design, not a single API operation or a promise of immediate session revocation. Verify each supported membership/account action.

```mermaid theme={null}
graph LR
    A[Active User] --> B[Remove from Teams]
    B --> C[Disable Access]
    C --> D[Archive Data]
    D --> E[Deactivated]
```

## Best Practices

1. **Use strong passwords** - Enforce password complexity requirements
2. **Regular access reviews** - Audit user permissions periodically
3. **Minimize admin users** - Only essential personnel should have admin access
4. **Complete profiles** - Ensure all user information is up-to-date
5. **Use appropriate roles** - Follow principle of least privilege
6. **Monitor user activity** - Track login and action patterns
7. **Clean up inactive users** - Remove or disable unused accounts

## Security Considerations

### Password Requirements

This API's user-creation route generates a password internally and does not accept
an application-supplied password. These endpoints do not establish a password-length,
complexity or password-history policy for your application. Use the documented reset
flow where available rather than transmitting passwords through profile fields.

### Session Management

A generated login link carries a credential. Deliver it only to its intended user;
do not log or expose it in analytics. Use its returned `valid_until` value for the
link's lifetime. This API guide makes no guarantee about browser-session inactivity
limits or refresh-token lifetimes; those are distinct from link expiration.

## Related Resources

* [Teams API](/guides/teams) - Manage team membership
* [gigstack Connect](/guides/gigstack-connect) - Multi-team user access
* [Clients API](/guides/clients) - Users create and manage clients
* [Invoices API](/guides/invoices) - User permissions affect invoice access

## Error Handling

### User Already Exists

```json theme={null}
{
    "message": "User creation failed",
    "error": "Email address already registered"
}
```

### Invalid Email Format

```json theme={null}
{
    "message": "Invalid request",
    "error": "Email format is invalid"
}
```

### User Not Found

```json theme={null}
{
    "message": "User not found",
    "error": "The specified user does not exist"
}
```

### Insufficient Permissions

```json theme={null}
{
    "message": "Access denied",
    "error": "Admin role required to manage users"
}
```

### Password Reset Failed

```json theme={null}
{
    "message": "Password reset failed",
    "error": "No user found with that email address"
}
```

### Login Link Generation Failed

**User Not Created via API:**

```json theme={null}
{
    "message": "User not found or not accessible via API",
    "error": "This user was not created through the API or does not exist"
}
```

**User Not in Billing Account:**

```json theme={null}
{
    "message": "Access denied",
    "error": "User does not belong to your billing account"
}
```

***

For additional assistance, contact [support@gigstack.io](mailto:support@gigstack.io)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.