openapi: 3.1.0
info:
    version: '0.0'
    title: 'Qomon Users API'
    description: "<h2>Welcome to our Users API documentation.</h2>\nThis is Qomon's API definition.\n\nTo get an authorization go to [Qomon's setting page](https://qomon.app/settings/extensions/connect) and create an API key.\n<br>\n\n---\n\n<br>\n<h2>User object:</h2>\nA user is defined by their personal information:\n<br>\n```\n{\n  \"firstname\": \"John\",\n  \"surname\": \"Doe\",\n  \"mail\": \"john.doe@example.com\",\n  \"mobile\": \"0123456789\",\n  \"address\": {\n    \"housenumber\": \"123\",\n    \"street\": \"Rue de la paix\",\n    \"city\": \"Paris\",\n    \"country\": \"France\"\n  }\n}\n```\nAnd some qomon specific attributes: \n\n```\n{\n  \"role\": [\n    { \"superadmin\", \"admin\", \"manager\", \"user\", \"custom\" }\n  ],\n  \"status\": [\n    { \"available week-end\", \"available night\" }\n  ],\n}\n```\nThe space information is retrieved from the token used to authenticate the user.\n\n<BR>It is possible to : \n- invite users by email and manage their invitations\n- list users in the current space, edit their informations and remove them\n- list all the roles and add a role to a user\n- list, create, update and delete teams (public or not)\n\n<h3>💡 Tips for Using the API:</h3>\n- <b>Roles gestion:</b><br>\nEach space has different roles and their corresponding role_id.\nYou can retrieve the list of roles by calling the <code>/roles</code> endpoint.\n- <b>Team privacy:</b><br>\nTeams can be public or private. Public teams are visible to all users in the space, while private teams are only visible to their members."
servers:
    -
        description: 'Qomon API'
        url: 'https://incoming.qomon.app'
security:
    -
        bearerAuth: []
tags:
    -
        name: Invitation
        description: 'Invite users'
    -
        name: User
        description: 'Manage users'
    -
        name: Role
        description: 'List roles'
    -
        name: Team
        description: 'Create, Read, Update and Delete teams'
    -
        name: Notification
        description: 'Send global messages to teams'
paths:
    /users/invitation:
        post:
            tags: [Invitation]
            summary: 'Invite users by their emails.'
            description: "Send an invitation to the specified mail addresses.\nEach space has different roles and their corresponding `role_id`.\nIf the mail address is already registered, the user will be added to the space.\nIf the mail address is not registered, the user will be invited to join the space.\n\n> 💡 Not specifying a `role_id` will set the role \"user\" by default."
            requestBody: {required: true, content: {application/json: {schema: {type: object, required: [data], properties: {data: {type: object, required: [invitations], properties: {invitations: {type: array, items: {required: [email], properties: {email: {type: string, format: email, description: 'Email to invite.', example: example@mail.com}, role_id: {type: integer, format: uint, description: 'Role ID.', example: 8}}}}}}}}}}}
            responses: {'201': {description: Created, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {msg: {type: string, example: 'invitations created successfully'}, invitations: {type: object, properties: {id: {type: integer, format: uint, example: 101}, group_id: {type: integer, format: uint, example: 101}, email: {type: string, format: email, example: johndoe@mail.com}, mobile_phone: {type: string, example: '0123456789'}, role_id: {type: integer, format: uint, example: 5}, role: {$ref: '#/components/schemas/Role'}, created_at: {type: string, format: date-time, example: '1955-07-06T00:00:00Z'}, last_sent: {type: string, format: date-time, example: '1955-07-06T00:00:00Z'}, user_id: {type: integer, format: uint, nullable: true, example: 2345}}}}}}}}}}, '207': {description: 'Multi-Status - Some invitations created successfully', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '400': {description: 'Bad Request - Invalid request data', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '405': {description: 'Method Not Allowed', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
        get:
            tags: [Invitation]
            summary: 'Retrieve all invitations.'
            description: "Get all invitations in the sender-selected space.\nThe list contains both accepted and pending invitations."
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {invitations: {type: object, properties: {id: {type: integer, format: uint, example: 101}, group_id: {type: integer, format: uint, example: 101}, email: {type: string, format: email, example: johndoe@mail.com}, mobile_phone: {type: string, example: '0123456789'}, role_id: {type: integer, format: uint, example: 5}, role: {$ref: '#/components/schemas/Role'}, created_at: {type: string, format: date-time, example: '1955-07-06T00:00:00Z'}, last_sent: {type: string, format: date-time, example: '1955-07-06T00:00:00Z'}, user_id: {type: integer, format: uint, nullable: true, example: 2345}}}}}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
    '/users/invitation/{id}':
        delete:
            tags: [Invitation]
            summary: 'Delete an invitation.'
            description: 'Delete the invitation with the given invitation id.'
            parameters: [{name: id, in: path, description: 'user id', required: true, schema: {type: integer, format: uint, example: 111}}]
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {message: {type: string, example: 'Invitation deleted successfully'}}}}}}}}, '400': {description: 'Bad Request - Invalid invitation ID', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '404': {description: 'Not Found - Invitation does not exist or does not belong to this group', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '405': {description: 'Method Not Allowed', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
    /users:
        get:
            tags: [User]
            summary: 'Retrieve all users.'
            description: "Returns all users from the sender selected-space.\n\n> 💡 Response can be filtered using 3 parameters:\n- **limit**: The maximum number of users to return.\n- **offset**: The starting index for the results (for pagination).\n- **sort**: Whether results are sorted ascending (true) or descending (false) by surname then firstname. Default: true."
            parameters: [{name: limit, in: query, schema: {type: integer, format: uint, minimum: 1}, required: false, description: 'Optional. The maximum number of users to return.', example: 20}, {name: offset, in: query, schema: {type: integer, format: uint, minimum: 0}, required: false, description: 'Optional. The starting index for the results (for pagination).', example: 0}, {name: sort, in: query, schema: {type: boolean}, required: false, description: 'Optional. Indicates whether the results should be sorted ascending (true) or descending (false) by surname then firstname. Default value is true.', example: true}]
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {count: {type: integer, format: uint, description: 'Number of users returned.', example: 100}, teams: {type: string, example: null}, users: {type: array, items: {type: object, properties: {created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the user was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the user was last updated (ISO 8601 format, UTC).'}, deleted_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the user was soft deleted (ISO 8601 format, UTC).'}, address: {type: string, example: '1 rue de la paix', description: 'The user''s street address.'}, birthdate: {type: string, format: date-time, example: '1999-03-06T23:00:00Z', description: 'The user''s birthdate (ISO 8601 format, UTC).'}, city: {type: string, example: Paris, description: 'The user''s city.'}, created: {type: string, format: date-time, example: '2025-04-23T08:16:26.088746Z', description: 'Another timestamp indicating creation (ISO 8601 format, UTC).'}, firstname: {type: string, example: John, description: 'The user''s first name.'}, group_id: {type: integer, format: uint, example: 1, description: 'The ID of the user''s space.'}, id: {type: integer, format: uint, example: 101, description: 'The unique identifier for the user.'}, locale: {type: string, example: en, description: 'The user''s locale language.'}, location: {type: string, example: '1.826483,-1.549486', description: 'The user''s geographical coordinates (latitude,longitude as string).'}, mail: {type: string, format: email, example: test@qomon.com, description: 'The user''s email address.'}, phone: {type: string, example: '0123456789', description: 'The user''s phone number.'}, postal: {type: string, example: '75000', description: 'The user''s postal code.'}, role: {type: string, example: user, description: 'The user''s role.'}, role_data: {type: object, properties: {id: {type: integer, format: uint, example: 5, description: 'The ID of the role data.'}, name: {type: string, example: user, description: 'The name of the user''s role.'}, order: {type: integer, format: uint, example: 1, description: 'The order of the role.'}, type: {type: string, example: user, description: 'The type of the user''s role.'}}, description: 'Detailed data about the user''s role.'}, selected_group_id: {type: array, items: {type: integer, format: uint}, example: [1], description: 'A list of selected space IDs for the user.'}, surname: {type: string, example: DohnJoe, description: 'The user''s surname'}}}}}}}}}}}, '404': {description: 'Not Found - No users found', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
    '/users/{id}':
        get:
            tags: [User]
            summary: 'Get a user''s information.'
            description: 'Retrieve the user with the given ID.'
            parameters: [{name: id, in: path, description: 'user id', required: true, schema: {type: integer, format: uint, example: 111}}]
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {User: {type: object, properties: {created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the user was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the user was last updated (ISO 8601 format, UTC).'}, deleted_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the user was soft deleted (ISO 8601 format, UTC).'}, address: {type: string, example: '1 rue de la paix', description: 'The user''s street address.'}, birthdate: {type: string, format: date-time, example: '1999-03-06T23:00:00Z', description: 'The user''s birthdate (ISO 8601 format, UTC).'}, city: {type: string, example: Paris, description: 'The user''s city.'}, created: {type: string, format: date-time, example: '2025-04-23T08:16:26.088746Z', description: 'Another timestamp indicating creation (ISO 8601 format, UTC).'}, firstname: {type: string, example: John, description: 'The user''s first name.'}, group_id: {type: integer, format: uint, example: 1, description: 'The ID of the user''s space.'}, id: {type: integer, format: uint, example: 101, description: 'The unique identifier for the user.'}, locale: {type: string, example: en, description: 'The user''s locale language.'}, location: {type: string, example: '1.826483,-1.549486', description: 'The user''s geographical coordinates (latitude,longitude as string).'}, mail: {type: string, format: email, example: test@qomon.com, description: 'The user''s email address.'}, phone: {type: string, example: '0123456789', description: 'The user''s phone number.'}, postal: {type: string, example: '75000', description: 'The user''s postal code.'}, role: {type: string, example: user, description: 'The user''s role.'}, role_data: {type: object, properties: {id: {type: integer, format: uint, example: 5, description: 'The ID of the role data.'}, name: {type: string, example: user, description: 'The name of the user''s role.'}, order: {type: integer, format: uint, example: 1, description: 'The order of the role.'}, type: {type: string, example: user, description: 'The type of the user''s role.'}}, description: 'Detailed data about the user''s role.'}, selected_group_id: {type: array, items: {type: integer, format: uint}, example: [1], description: 'A list of selected space IDs for the user.'}, surname: {type: string, example: DohnJoe, description: 'The user''s surname'}}}}}}}}}}, '400': {description: 'Bad Request - Invalid user ID', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '403': {description: 'Forbidden - Missing permissions or invalid group ID', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '404': {description: 'Not Found - User does not exist', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
        patch:
            tags: [User]
            summary: 'Update a user with the given informations.'
            description: "Update provided fields for the user with the corresponding ID.\nThe user's email address is mandatory and must be valid.\n\n> 💡 To change the `selected_group_id` it is mandatory to set `UpdateToken` to true."
            parameters: [{name: id, in: path, description: 'user id', required: true, schema: {type: integer, format: uint, example: 111}}]
            requestBody: {description: 'The new values of the user.', required: true, content: {application/json: {schema: {required: [data], type: object, properties: {data: {type: object, properties: {mail: {type: string, format: email, description: 'The email address of the user.'}, surname: {type: string, example: DohnJoe, description: 'The user''s surname.'}, firstname: {type: string, example: John, description: 'The user''s first name.'}, phone: {type: string, example: '0123456789', description: 'The user''s phone number.'}, address: {type: string, example: '1 rue de la paix', description: 'The user''s street address.'}, city: {type: string, example: Paris, description: 'The user''s city.'}, postal: {type: string, example: '75000', description: 'The user''s postal code.'}, location: {type: string, example: '1.826483,-1.549486', description: 'The user''s geographical coordinates (latitude,longitude as string).'}, avatar: {type: string, format: uri, example: 'http://example.com/image.png'}, birthdate: {type: string, format: date-time, example: '1999-03-06T23:00:00Z', description: 'The user''s birthdate (ISO 8601 format, UTC).'}}, required: [mail]}}}}}}
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: string, example: 'Successfully updated'}}}}}}, '400': {description: 'Bad Request - Invalid user ID or email not provided', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '401': {description: 'Unauthorized - You are not authorized to update this user', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '404': {description: 'Not Found - User does not exist', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
        delete:
            tags: [User]
            summary: 'Remove the given user from the sender-selected space.'
            description: 'Retrieve the sender-selected space id and remove the user id given in parameter.'
            parameters: [{name: id, in: path, description: 'user id', required: true, schema: {type: integer, format: uint, example: 111}}]
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: string, example: OK}}}}}}, '400': {description: 'Bad Request - Invalid user ID', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '404': {description: 'Not Found - User does not exist', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
    /users/role:
        patch:
            tags: [User]
            summary: 'Edit user''s role.'
            description: "Update the role of the user with the given ID.\nThe `role_id` must be provided in the request body."
            requestBody: {required: true, content: {application/json: {schema: {required: [data], type: object, properties: {data: {type: object, required: [user_id, role_id], properties: {user_id: {type: integer, format: uint, example: 111, description: 'The user''s ID.'}, role_id: {type: integer, format: uint, example: 8, description: 'The role''s ID.'}}}}}}}}
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: string, example: 'Successfully updated'}}}}}}, '400': {description: 'Bad Request - Invalid user ID or role ID', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '403': {description: 'Forbidden - Missing permissions or insufficient role', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
    /roles:
        get:
            tags: [Role]
            summary: 'Retrieve role collection.'
            description: 'Retrieve all roles available in the sender-selected space.'
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {count: {type: integer, example: 1}, roles: {type: array, items: {allOf: [{$ref: '#/components/schemas/RoleWithoutAccessList'}]}}}}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
    /teams:
        get:
            tags: [Team]
            summary: 'Retrieve all teams.'
            description: "Returns every team the caller has access to.\n\nAn optional `user_id` query parameter filters the result to teams\nthe target user belongs to. Only users reachable by the caller are\nconsidered — any `user_id` outside that scope simply yields an\nempty list."
            parameters: [{name: user_id, in: query, required: false, description: 'When set, return only teams the user with this ID belongs to.', schema: {type: integer, format: uint, minimum: 1, example: 111}}]
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {teams: {type: array, nullable: true, items: {type: object, properties: {id: {type: integer, format: uint, description: 'Team ID', example: 101}, created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the team was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the team was last updated (ISO 8601 format, UTC).'}, name: {type: string, example: 'Team Number1'}, description: {type: string, example: 'This is a sample team description.'}, private: {type: boolean, description: 'When true, the team is only visible to its members.', example: false}, hide_users: {type: boolean, description: 'When true, the team''s users list is hidden to non-members.', example: false}, leaders: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, users: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, address: {$ref: '#/components/schemas/Address'}}, required: [id, created_at, updated_at, name]}}}}}}}}}, '400': {description: 'Bad Request - Invalid request data', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}}
        post:
            tags: [Team]
            summary: 'Create a new team.'
            description: "Creates a new team.\n`users` may be an empty array to create an empty team.\n`leaders` must be a subset of `users`."
            requestBody: {required: true, content: {application/json: {schema: {type: object, required: [data], properties: {data: {type: object, required: [team], properties: {team: {type: object, required: [name, users], properties: {name: {type: string, description: 'The team''s display name.', example: 'Team Number1'}, description: {type: string, description: 'Optional free-form description.', example: 'Volunteers for district 3'}, private: {type: boolean, default: false, description: 'When true, the team is only visible to its members.'}, hide_users: {type: boolean, default: false, description: 'When true, the team''s users list is hidden to non-members.'}, users: {type: array, description: 'Users that should belong to the team. Pass `[]` to create an empty team.', items: {$ref: '#/components/schemas/TeamMemberRef'}}, leaders: {type: array, description: 'Subset of `users` who should be team leaders.', items: {$ref: '#/components/schemas/TeamMemberRef'}}, address: {$ref: '#/components/schemas/Address'}}}}}}}}}}
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {team: {type: object, properties: {id: {type: integer, format: uint, description: 'Team ID', example: 101}, created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the team was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the team was last updated (ISO 8601 format, UTC).'}, name: {type: string, example: 'Team Number1'}, description: {type: string, example: 'This is a sample team description.'}, private: {type: boolean, description: 'When true, the team is only visible to its members.', example: false}, hide_users: {type: boolean, description: 'When true, the team''s users list is hidden to non-members.', example: false}, leaders: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, users: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, address: {$ref: '#/components/schemas/Address'}}, required: [id, created_at, updated_at, name]}}}}}}}}, '400': {description: 'Bad Request - Invalid request body (JSON schema validation failed).', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '403': {description: 'Forbidden - Insufficient permissions.', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}}
        patch:
            tags: [Team]
            summary: 'Update an existing team (full replace).'
            description: "Updates the team with the given ID.\n\n⚠️ **This is a full replace, not a partial merge.** Any field you omit\n(except `name`) is cleared or reset to its default on the server.\nSee the request body schema for the exact per-field behavior.\n\nA safe workflow is to `GET /teams`, find the team you want to edit,\nmodify the fields you need, then `PATCH` it back here as a whole."
            requestBody: {required: true, content: {application/json: {schema: {type: object, required: [data], properties: {data: {type: object, required: [team], properties: {team: {type: object, description: "⚠️ This endpoint **replaces** the team — it is not a partial merge.\nAny field you omit (except `name`) is cleared or reset on the server:\n- `name` → preserved when omitted\n- `description` → reset to `\"\"`\n- `private` → reset to `false`\n- `hide_users` → reset to `false`\n- `users` → cleared (all members removed)\n- `leaders` → cleared\n- `address` → cleared\n\nIn practice, re-send the team as a whole. A safe workflow is to GET the\nteam from `/teams`, modify the fields you need, and send the full object back here.", required: [id], properties: {id: {type: integer, format: uint, description: 'The ID of the team to update.', example: 1}, name: {type: string, example: 'Renamed team'}, description: {type: string, example: 'This is a sample team description.'}, private: {type: boolean, example: false}, hide_users: {type: boolean, example: false}, users: {type: array, items: {$ref: '#/components/schemas/TeamMemberRef'}}, leaders: {type: array, items: {$ref: '#/components/schemas/TeamMemberRef'}}, address: {$ref: '#/components/schemas/Address'}}}}}}}}}}
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {team: {type: object, properties: {id: {type: integer, format: uint, description: 'Team ID', example: 101}, created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the team was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the team was last updated (ISO 8601 format, UTC).'}, name: {type: string, example: 'Team Number1'}, description: {type: string, example: 'This is a sample team description.'}, private: {type: boolean, description: 'When true, the team is only visible to its members.', example: false}, hide_users: {type: boolean, description: 'When true, the team''s users list is hidden to non-members.', example: false}, leaders: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, users: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, address: {$ref: '#/components/schemas/Address'}}, required: [id, created_at, updated_at, name]}}}}}}}}, '400': {description: 'Bad Request - Invalid request body (JSON schema validation failed).', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '403': {description: 'Forbidden - Insufficient permissions.', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}, '404': {description: 'Not Found - Team does not exist.', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}}
    '/teams/{id}':
        get:
            tags: [Team]
            summary: 'Retrieve a team.'
            description: 'Returns the team with the given ID.'
            parameters: [{name: id, in: path, description: 'The ID of the team to retrieve.', required: true, schema: {type: integer, format: uint, minimum: 1, example: 1}}]
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {team: {type: object, properties: {id: {type: integer, format: uint, description: 'Team ID', example: 101}, created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the team was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the team was last updated (ISO 8601 format, UTC).'}, name: {type: string, example: 'Team Number1'}, description: {type: string, example: 'This is a sample team description.'}, private: {type: boolean, description: 'When true, the team is only visible to its members.', example: false}, hide_users: {type: boolean, description: 'When true, the team''s users list is hidden to non-members.', example: false}, leaders: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, users: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, address: {$ref: '#/components/schemas/Address'}}, required: [id, created_at, updated_at, name]}}}}}}}}, '400': {description: 'Bad Request - Invalid team ID.', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '404': {description: 'Not Found - Team does not exist.', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}}
        delete:
            tags: [Team]
            summary: 'Delete a team.'
            description: "Soft-deletes the team with the given ID.\n\nAttempting to delete a team that does not exist returns `404 Not\nFound`. Any other failure returns `500 Internal Server Error`."
            parameters: [{name: id, in: path, description: 'The ID of the team to delete.', required: true, schema: {type: integer, format: uint, example: 1}}]
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {message: {type: string, example: 'Team successfully deleted'}, team_id: {type: integer, format: uint, example: 1}}}}}}}}, '400': {description: 'Bad Request - Invalid team ID.', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '403': {description: 'Forbidden - Insufficient permissions.', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}, '404': {description: 'Not Found - Team does not exist.', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}}
    /teams/public:
        get:
            tags: [Team]
            summary: 'Retrieve public teams.'
            description: 'Returns only the public teams the caller has access to.'
            responses: {'200': {description: OK, content: {application/json: {schema: {type: array, items: {type: object, properties: {id: {type: integer, format: uint, description: 'Team ID', example: 101}, created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the team was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the team was last updated (ISO 8601 format, UTC).'}, name: {type: string, example: 'Team Number1'}, description: {type: string, example: 'Public outreach team'}, leaders: {type: array, items: {$ref: '#/components/schemas/User'}}, users: {type: array, items: {$ref: '#/components/schemas/User'}}, hide_users: {type: boolean, description: 'When true, the team''s users list is hidden to non-members.'}, address: {$ref: '#/components/schemas/Address'}}, required: [id, created_at, updated_at, name]}}}}}, '400': {description: 'Bad Request - Invalid request data', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …).", properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}}}}}}
    /global-message:
        post:
            tags: [Notification]
            summary: 'Send a global message to provided teams.'
            description: 'Sends a global message to the specified teams in the sender-selected space.'
            requestBody: {required: true, content: {application/json: {schema: {type: object, properties: {data: {type: object, properties: {body: {type: string, description: 'The message body.', example: 'Hello, this is a global message.'}, title: {type: string, description: 'The message title.', example: 'Global Message'}, team_ids: {type: array, items: {type: integer}, description: 'The IDs of the teams to send the message to.', example: [1, 2, 3]}}}}}}}}
            responses: {'200': {description: OK, content: {application/json: {schema: {type: object, properties: {status: {type: string, example: success}, data: {type: object, properties: {accepted: {type: boolean, example: true}}}}}}}}, '400': {description: 'Bad Request - Invalid request data', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}, '500': {description: 'Internal Server Error', content: {application/json: {schema: {type: object, description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …).", properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}}}}}}
components:
    securitySchemes:
        bearerAuth:
            type: http
            scheme: bearer
    responses:
        BadRequest:
            description: 'Invalid request, such as lacking required request body or parameter'
            content: {application/json: {schema: {$ref: '#/components/schemas/GenericErrorResponse'}}}
        Forbidden:
            description: 'Forbidden - insufficient permissions'
            content: {application/json: {schema: {$ref: '#/components/schemas/GenericErrorResponse'}}}
        NotFound:
            description: 'Resource not found'
            content: {application/json: {schema: {$ref: '#/components/schemas/GenericErrorResponse'}}}
        UnexpectedError:
            description: 'Internal server error'
            content: {application/json: {schema: {$ref: '#/components/schemas/GenericErrorResponse'}}}
    schemas:
        GenericErrorResponse:
            type: object
            description: "Client-side validation failure envelope returned with HTTP 4xx when the\nrequest payload is malformed (missing/invalid fields, bad JSON, …)."
            properties: {status: {type: string, example: fail, description: 'Indicates the operation status.'}, data: {type: object, description: 'Map of validation errors, keyed by offending field name.'}}
        ErrorResponse:
            type: object
            description: "Server-side error envelope returned with HTTP 404 or 5xx when an\noperation cannot be fulfilled (missing resource, downstream\nfailure, …)."
            properties: {status: {type: string, example: error, description: 'Indicates the operation status.'}, message: {type: string, example: 'record not found', description: 'Human-readable explanation of the failure.'}}
        UserRolePatchRequest:
            type: object
            required: [user_id, role_id]
            properties: {user_id: {type: integer, format: uint, example: 111, description: 'The user''s ID.'}, role_id: {type: integer, format: uint, example: 8, description: 'The role''s ID.'}}
        UserPatchRequest:
            type: object
            properties: {mail: {type: string, format: email, description: 'The email address of the user.'}, surname: {type: string, example: DohnJoe, description: 'The user''s surname.'}, firstname: {type: string, example: John, description: 'The user''s first name.'}, phone: {type: string, example: '0123456789', description: 'The user''s phone number.'}, address: {type: string, example: '1 rue de la paix', description: 'The user''s street address.'}, city: {type: string, example: Paris, description: 'The user''s city.'}, postal: {type: string, example: '75000', description: 'The user''s postal code.'}, location: {type: string, example: '1.826483,-1.549486', description: 'The user''s geographical coordinates (latitude,longitude as string).'}, avatar: {type: string, format: uri, example: 'http://example.com/image.png'}, birthdate: {type: string, format: date-time, example: '1999-03-06T23:00:00Z', description: 'The user''s birthdate (ISO 8601 format, UTC).'}}
            required: [mail]
        User:
            type: object
            properties: {created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the user was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the user was last updated (ISO 8601 format, UTC).'}, deleted_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the user was soft deleted (ISO 8601 format, UTC).'}, address: {type: string, example: '1 rue de la paix', description: 'The user''s street address.'}, birthdate: {type: string, format: date-time, example: '1999-03-06T23:00:00Z', description: 'The user''s birthdate (ISO 8601 format, UTC).'}, city: {type: string, example: Paris, description: 'The user''s city.'}, created: {type: string, format: date-time, example: '2025-04-23T08:16:26.088746Z', description: 'Another timestamp indicating creation (ISO 8601 format, UTC).'}, firstname: {type: string, example: John, description: 'The user''s first name.'}, group_id: {type: integer, format: uint, example: 1, description: 'The ID of the user''s space.'}, id: {type: integer, format: uint, example: 101, description: 'The unique identifier for the user.'}, locale: {type: string, example: en, description: 'The user''s locale language.'}, location: {type: string, example: '1.826483,-1.549486', description: 'The user''s geographical coordinates (latitude,longitude as string).'}, mail: {type: string, format: email, example: test@qomon.com, description: 'The user''s email address.'}, phone: {type: string, example: '0123456789', description: 'The user''s phone number.'}, postal: {type: string, example: '75000', description: 'The user''s postal code.'}, role: {type: string, example: user, description: 'The user''s role.'}, role_data: {type: object, properties: {id: {type: integer, format: uint, example: 5, description: 'The ID of the role data.'}, name: {type: string, example: user, description: 'The name of the user''s role.'}, order: {type: integer, format: uint, example: 1, description: 'The order of the role.'}, type: {type: string, example: user, description: 'The type of the user''s role.'}}, description: 'Detailed data about the user''s role.'}, selected_group_id: {type: array, items: {type: integer, format: uint}, example: [1], description: 'A list of selected space IDs for the user.'}, surname: {type: string, example: DohnJoe, description: 'The user''s surname'}}
        RoleWithoutAccessList:
            type: object
            properties: {id: {type: integer, format: uint, example: 5}, name: {type: string}, type: {type: string, enum: [superadmin, admin, manager, user, custom]}, order: {type: integer, format: uint, description: "Displayed order for roles in role management view.\nSorted in ascending excluding 0 which correspond to last items."}}
        Role:
            allOf: [{$ref: '#/components/schemas/RoleWithoutAccessList'}]
        PublicTeam:
            type: object
            properties: {id: {type: integer, format: uint, description: 'Team ID', example: 101}, created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the team was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the team was last updated (ISO 8601 format, UTC).'}, name: {type: string, example: 'Team Number1'}, description: {type: string, example: 'Public outreach team'}, leaders: {type: array, items: {$ref: '#/components/schemas/User'}}, users: {type: array, items: {$ref: '#/components/schemas/User'}}, hide_users: {type: boolean, description: 'When true, the team''s users list is hidden to non-members.'}, address: {$ref: '#/components/schemas/Address'}}
            required: [id, created_at, updated_at, name]
        TeamUser:
            type: object
            description: "Projection of a user as embedded in a `Team`. Internal or\nidentity/authorization fields (`group_id`, `selected_group_id`,\n`role_data.web`, `role_data.mobile`, `role_data.group_id`, …) are\nintentionally stripped from this view and are never returned."
            required: [id]
            properties: {id: {type: integer, format: uint, example: 101, description: 'The user''s ID.'}, firstname: {type: string, example: John}, surname: {type: string, example: Doe}, mail: {type: string, format: email, example: john.doe@example.com}, avatar: {type: string, format: uri, example: 'https://example.com/avatar.png'}, phone: {type: string, example: '0123456789'}, role: {type: string, example: user, description: 'Short-hand alias for `role_data.type`.'}, role_data: {type: object, description: 'Public subset of the user''s role.', properties: {id: {type: integer, format: uint, example: 5}, name: {type: string, example: User}, color: {type: string, example: '#3b82f6'}, type: {type: string, enum: [superadmin, admin, manager, user, custom], example: user}, order: {type: integer, format: uint, example: 1}}}}
        Team:
            type: object
            properties: {id: {type: integer, format: uint, description: 'Team ID', example: 101}, created_at: {type: string, format: date-time, example: '2025-04-23T08:16:26.08912Z', description: 'The timestamp when the team was created (ISO 8601 format, UTC).'}, updated_at: {type: string, format: date-time, example: '2025-04-30T09:55:59.320084Z', description: 'The timestamp when the team was last updated (ISO 8601 format, UTC).'}, name: {type: string, example: 'Team Number1'}, description: {type: string, example: 'This is a sample team description.'}, private: {type: boolean, description: 'When true, the team is only visible to its members.', example: false}, hide_users: {type: boolean, description: 'When true, the team''s users list is hidden to non-members.', example: false}, leaders: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, users: {type: array, items: {$ref: '#/components/schemas/TeamUser'}}, address: {$ref: '#/components/schemas/Address'}}
            required: [id, created_at, updated_at, name]
        TeamMemberRef:
            type: object
            description: 'Reference to an existing user by ID. Only `id` is read by the backend.'
            required: [id]
            properties: {id: {type: integer, format: uint, example: 7}}
        TeamCreateRequest:
            type: object
            required: [name, users]
            properties: {name: {type: string, description: 'The team''s display name.', example: 'Team Number1'}, description: {type: string, description: 'Optional free-form description.', example: 'Volunteers for district 3'}, private: {type: boolean, default: false, description: 'When true, the team is only visible to its members.'}, hide_users: {type: boolean, default: false, description: 'When true, the team''s users list is hidden to non-members.'}, users: {type: array, description: 'Users that should belong to the team. Pass `[]` to create an empty team.', items: {$ref: '#/components/schemas/TeamMemberRef'}}, leaders: {type: array, description: 'Subset of `users` who should be team leaders.', items: {$ref: '#/components/schemas/TeamMemberRef'}}, address: {$ref: '#/components/schemas/Address'}}
        TeamPatchRequest:
            type: object
            description: "⚠️ This endpoint **replaces** the team — it is not a partial merge.\nAny field you omit (except `name`) is cleared or reset on the server:\n- `name` → preserved when omitted\n- `description` → reset to `\"\"`\n- `private` → reset to `false`\n- `hide_users` → reset to `false`\n- `users` → cleared (all members removed)\n- `leaders` → cleared\n- `address` → cleared\n\nIn practice, re-send the team as a whole. A safe workflow is to GET the\nteam from `/teams`, modify the fields you need, and send the full object back here."
            required: [id]
            properties: {id: {type: integer, format: uint, description: 'The ID of the team to update.', example: 1}, name: {type: string, example: 'Renamed team'}, description: {type: string, example: 'This is a sample team description.'}, private: {type: boolean, example: false}, hide_users: {type: boolean, example: false}, users: {type: array, items: {$ref: '#/components/schemas/TeamMemberRef'}}, leaders: {type: array, items: {$ref: '#/components/schemas/TeamMemberRef'}}, address: {$ref: '#/components/schemas/Address'}}
        InvitationResult:
            type: object
            properties: {id: {type: integer, format: uint, example: 101}, group_id: {type: integer, format: uint, example: 101}, email: {type: string, format: email, example: johndoe@mail.com}, mobile_phone: {type: string, example: '0123456789'}, role_id: {type: integer, format: uint, example: 5}, role: {$ref: '#/components/schemas/Role'}, created_at: {type: string, format: date-time, example: '1955-07-06T00:00:00Z'}, last_sent: {type: string, format: date-time, example: '1955-07-06T00:00:00Z'}, user_id: {type: integer, format: uint, nullable: true, example: 2345}}
        Address:
            type: object
            properties: {housenumber: {type: string, example: '1'}, street: {type: string, example: 'rue de la paix'}, postalcode: {type: string, example: '75000'}, citycode: {type: string, example: '75101'}, city: {type: string, example: Paris}, state: {type: string, example: Ile-de-France}, country: {type: string, example: France}, addition: {type: string, example: 'Résidence de la paix'}, infos: {type: string, example: 'Porte de gauche'}, building: {type: string, example: 'Bâtiment A'}, floor: {type: string, example: '2'}, door: {type: string, example: A}, pollingstation: {type: string, example: BV-06}, score: {type: number, format: float, minimum: 0, maximum: 1, example: 0.75, nullable: true}, building_type: {type: string, example: house}, lattitude: {type: string, example: '89.900000'}, longitude: {type: string, example: '-0.587877'}, location: {type: string, example: '89.900000, -0.587877'}, invalid: {type: boolean, nullable: true}}
