diff --git a/README.md b/README.md
index d65a09e..934e845 100644
--- a/README.md
+++ b/README.md
@@ -1,2243 +1,19 @@
-# 4D NetKit
+
+
+# Documentation
+
-## Overview
4D NetKit is a built-in 4D component that allows you to interact with third-party web services and their APIs, such as [Microsoft Graph](https://docs.microsoft.com/en-us/graph/overview).
## Table of contents
-* [OAuth2Provider class](#oauth2provider)
- - [New OAuth2 provider](#new-oauth2-provider)
- - [OAuth2ProviderObject.getToken()](#oauth2providerobjectgettoken)
-* [Office365 class](#office365)
- - [New Office365 provider](#new-office365-provider)
- - [Office365.mail.append()](#office365mailappend)
- - [Office365.mail.copy()](#office365mailcopy)
- - [Office365.mail.createFolder()](#office365mailcreatefolder)
- - [Office365.mail.delete()](#office365maildelete)
- - [Office365.mail.deleteFolder()](#office365maildeletefolder)
- - [Office365.mail.getFolder()](#office365mailgetfolder)
- - [Office365.mail.getFolderList()](#office365mailgetfolderlist)
- - [Office365.mail.getMail()](#office365mailgetmail)
- - [Office365.mail.getMails()](#office365mailgetmails)
- - [Office365.mail.move()](#office365mailmove)
- - [Office365.mail.renameFolder()](#office365mailrenameFolder)
- - [Office365.mail.reply()](#office365mailreply)
- - [Office365.mail.send()](#office365mailsend)
- - [Office365.mail.update()](#office365mailupdate)
- - [Well-known folder names](well-known-folder-names)
- - ["Microsoft" mail object properties](#microsoft-mail-object-properties)
- - [Status object (Office365 Class)](#status-object)
- - [Office365.user.get()](#office365userget)
- - [Office365.user.getCurrent()](#office365usergetcurrent)
- - [Office365.user.list()](#office365userlist)
-* [Google class](#google)
- - [cs.NetKit.Google.new()](#csnetkitgooglenew)
- - [Google.mail.append()](#googlemailappend)
- - [Google.mail.createLabel()](#googlemailcreatelabel)
- - [Google.mail.delete()](#googlemaildelete)
- - [Google.mail.deleteLabel()](#googlemaildeletelabel)
- - [Google.mail.getLabel()](#googlemailgetlabel)
- - [Google.mail.getLabelList()](#googlemailgetlabellist)
- - [Google.mail.getMail()](#googlemailgetmail)
- - [Google.mail.getMailIds()](#googlemailgetmailids)
- - [Google.mail.getMails()](#googlemailgetmails)
- - [Google.mail.send()](#googlemailsend)
- - [Google.mail.untrash()](#googlemailuntrash)
- - [Google.mail.update()](#googlemailupdate)
- - [Google.mail.updateLabel()](#googlemailupdatelabel)
- - [Google.user.get()](#googleuserget)
- - [Google.user.getCurrent()](#googleusergetcurrent)
- - [Google.user.list()](#googleuserlist)
- - [labelInfo object](#labelinfo-object)
- - [Status object (Google Class)](#status-object-google-class)
-
-* [Tutorial : Authenticate to the Microsoft Graph API in service mode](#authenticate-to-the-microsoft-graph-api-in-service-mode)
-* (Archived) [Tutorial : Authenticate to the Microsoft Graph API in signedIn mode (4D NetKit), then send an email (SMTP Transporter class)](#authenticate-to-the-microsoft-graph-api-in-signedin-mode-and-send-an-email-with-smtp)
-* [Copyrights](#copyrights)
-
-
-**Warning:** Shared objects are not supported by the 4D NetKit API.
-
-
-## OAuth2Provider
-
-The `OAuth2Provider` class allows you to request authentication tokens to third-party web services providers in order to use their APIs in your application. This is done in two steps:
-
-1. Using the `New OAuth2 provider` component method, you instantiate an object of the `OAuth2Provider` class that holds authentication information.
-2. You call the `OAuth2ProviderObject.getToken()` class function to retrieve a token from the web service provider.
-
-Here's a diagram of the authorization process:
-
-
-This class can be instantiated in two ways:
-* by calling the `New OAuth2 provider` method
-* by calling the `cs.NetKit.OAuth2Provider.new()` function
-
-
-**Warning:** OAuth2 authentication in `signedIn` mode requires a browser. Since some servers have restrictions regarding the supported browsers (for example, check this [Google support](https://support.google.com/accounts/answer/7675428?hl=en) page), the functionality may not work properly.
-
-### **New OAuth2 provider**
-
-**New OAuth2 provider**( *paramObj* : Object ) : cs.NetKit.OAuth2Provider
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|paramObj|Object|->| Determines the properties of the object to be returned |
-|Result|cs.NetKit.OAuth2Provider|<-| Object of the OAuth2Provider class|
-
-#### Description
-
-`New OAuth2 provider` instantiates an object of the `OAuth2Provider` class.
-
-In `paramObj`, pass an object that contains authentication information.
-
-
-The available properties of `paramObj` are:
-
-|Parameter|Type|Description|Optional|
-|---------|--- |------|------|
-| name | text | Name of the provider. Available values: "Microsoft", "Google" or "" (if "" or undefined/null attribute, the authenticateURI and the tokenURI need to be filled by the 4D developer).|Yes
-| permission | text |- "signedIn": Azure AD/Google will sign in the user and ensure they gave their consent for the permissions your app requests (opens a web browser).
- service": the app calls [Microsoft Graph with its own identity](https://docs.microsoft.com/en-us/graph/auth-v2-service)/Google (access without a user).|No
-| clientId | text | The client ID assigned to the app by the registration portal.|No
-| redirectURI | text | (Not used in service mode) The redirect_uri of your app, the location where the authorization server sends the user once the app has been successfully authorized. When you call the `.getToken()` class function, a web server included in 4D NetKit is started on the port specified in this parameter to intercept the provider's authorization response.|No in signedIn mode, Yes in service mode
-| scope | text or collection | Text: A space-separated list of the Microsoft Graph permissions that you want the user to consent to. Collection: Collection of Microsoft Graph permissions. |Yes
-| tenant | text | Microsoft: The {tenant} value in the path of the request can be used to control who can sign into the application. The allowed values are: - "common" for both Microsoft accounts and work or school accounts (default value)
- "organizations" for work or school accounts only
- "consumers" for Microsoft accounts only
- tenant identifiers such as tenant ID or domain name.
Google (service mode only): Email address to be considered as the email address of the user for which the application is requesting delegated access. |Yes
-| authenticateURI | text | Uri used to do the Authorization request.
Default for Microsoft: "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize".
Default for Google: "https://accounts.google.com/o/oauth2/auth". |Yes
-| tokenURI | text | Uri used to request an access token.
Default for Microsoft: "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token".
Default for Google: "https://accounts.google.com/o/oauth2/token".|Yes
-|tokenExpiration | text | Timestamp (ISO 8601 UTC) that indicates the expiration time of the token.| Yes
-| clientSecret | text | The application secret that you created for your app in the app registration portal. Required for web apps. |Yes
-| token | object | If this property exists, the `getToken()` function uses this token object to calculate which request must be sent. It is automatically updated with the token received by the `getToken()` function. |Yes
-| timeout|real| Waiting time in seconds (by default 120s).|Yes
-| prompt | text |(Optional) A space-delimited, case-sensitive list of prompts to present the user.
Possible values are:
- none: Do not display any authentication or consent screens. Must not be specified with other values.
- consent: Prompt the user for consent.
- select_account: Prompt the user to select an account.
(if you don't specify this parameter, the user will be prompted only the first time your project requests access. )|Yes|
-| loginHint | text | (Optional) This option can be used to inform the Google Authentication Server which user is attempting to authenticate if your application is aware of this information. By prefilling the email field in the sign-in form or by selecting the appropriate multi-login session, the server uses the hint to simplify the login flow either.
Set the parameter value to a sub-identifier or email address that corresponds to the user's Google ID. |Yes|
-| accessType | text | (Recommended) Indicates whether your application can refresh access tokens when the user is not present at the browser.
Valid parameter values are online (default) and offline.
Set the value to offline if your application needs to update access tokens when the user is not present at the browser. This is how access tokens are refreshed. This value instructs the Google authorization server to return a refresh token and an access token the first time that your application exchanges an authorization code for tokens. |Yes|
-| clientEmail | text | (mandatory, Google / service mode only) email address of the service account used |No|
-| authenticationPage|text or file object|Path of the web page to display in the web browser when the authentication code is received correctly in signed in mode (If not present the default page is used).|Yes
-| authenticationErrorPage |text or file object| Path of the web page to display in the web browser when the authentication server returns an error in signed in mode (If not present the default page is used).|Yes
-| PKCEEnabled |boolean| false by default. If true, PKCE is used for OAuth 2.0 authentication and token requests and is only usable for permission=”SignIn”. |Yes
-| PKCEMethod |text | "S256" by default. The only supported values for this parameter are "S256" or "plain". |Yes
-| thumbprint |text | Certificate thumbprint. Only usable with permission="Service" | Yes (No for certificate based authentication)
-| privateKey | text | Certificate private key. Only usable with permission="Service".
(Google / service mode only) Private key given by Google. Mandatory if .permission="service" and .name="Google" | Yes (No for certificate based authentication)
-| clientAssertionType | text | The format of the assertion as defined by the authorization server. The value is an absolute URI. Default value: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer". Only usable with permission="Service" |Yes
-| browserAutoOpen | boolean | True (default value), the web browser is open automatically. Pass false if you don't want the web browser to open automatically. |Yes
-
-If you want the .getToken() function to use the Assertion Framework described in the RFC 7521 to connect to the server, make sure to pass the `thumbprint` and `privateKey` properties. If `clientSecret`, `thumbprint` and `privateKey` are present, the `thumbprint` is used by default and the RFC 7521 is used to connect. For more information, please refer to the [OAuth2.0 authentication using a certificate](#https://blog.4d.com/) blog post.
-
-**Note:** The `authenticationPage` and `authenticationErrorPage` and all the resources associated must be in the same folder.
-
-#### Returned object
-
-The OAuth2 provider returned object `cs.NetKit.OAuth2Provider` properties correspond to those of the [`paramObj` object passed as a parameter](#description) and some additional properties:
-
-|Property|Type|Description|
-|----|-----|------|
-|*paramObj.properties*||< properties passed in parameter [`paramObj`](#description)>|
-|authenticateURI|text|Returns the calculated authenticateURI. Can be used in a webbrowser or in a web area to open the connection page.|
-|isTokenValid |Function| `OAuth2Provider.isTokenValid() : boolean`
Verifies the token validity.
- If no token is present, returns false.
- If the current token is not expired, returns true.
- If the token is expired and no refresh token is present, returns false.
- If a refresh token is present, automatically requests a new token and returns true if the token is generated correctly, otherwise false.|
-
-#### Example 1
-
-```4d
-
-//authentication into google account and token retrieval
-
-var $File1; $File2 : 4D.File
-var $oAuth2 : cs.NetKit.OAuth2Provider
-var $param: Object
-
-$File1:=File("/RESOURCES/OK.html")
-$File2:=File("/RESOURCES/KO.html")
-
-$param:= New object
-$param.name:="Google"
-$param.permission:="signedIn"
-$param.clientId:="xxxx"
-$param.clientSecret:="xxxx"
-$param.redirectURI:="http://127.0.0.1:50993/authorize/"
-$param.scope:="https://www.googleapis.com/auth/gmail.send"
-$param.authenticationPage:=$File1
-$param.authenticationErrorPage:=$File2
-// Create new OAuth2 object
-$oAuth2:=cs.NetKit.OAuth2Provider.new($param)
-// Ask for a token
-$token:=$oAuth2.getToken()
-
-```
-#### Example 2
-
-```4d
-
-//Google account authentication using PKCE
-
-var $credential:={}
-// google
-$credential.name:="Google"
-$credential.permission:="signedIn"
-$credential.clientId:="499730xxx"
-$credential.clientSecret:="fc1kwxxx"
-$credential.redirectURI:="http://127.0.0.1:50993/authorize/"
-$credential.scope:="https://mail.google.com/"
-// PKCE activation
-$credential.PKCEEnabled:=True
-
-var $oauth2:=cs.NetKit.OAuth2Provider.new($credential)
-var $token:=Try($oauth2.getToken())
-if ($token=null)
- ALERT("Error: "+Last errors[0].message)
-end if
-
-```
-
-#### Example 3
-
-```4d
-
-// Initial authentication with Microsoft OAuth2 and retrieval of token with refresh token
-
-// Define OAuth2 provider details for Microsoft
-$provider:=New object()
-$provider.name:="Microsoft"
-$provider.permission:="signedIn"
-$provider.clientId:="xxx-xxx-xxx-xxx-c460fc"
-$provider.redirectURI:="http://127.0.0.1:50993/authorize/"
-$provider.scope:="https://graph.microsoft.com/.default"
-
-// Use the "offline" parameter to request a refresh token in addition to the regular access token
-$provider.accessType:="offline"
-
-// Create new OAuth2 object for Microsoft
-$OAuth:= cs.NetKit.OAuth2Provider.new ($provider)
-
-// Request the token, which includes the refresh token
-var $myCurrentToken : Object := $OAuth.getToken()
-
-// After receiving the token and refresh token, save it for future token requests
-```
-
-
-```4d
-#DECLARE($myCurrentToken : object)
-var $provider:=New object()
-$provider.name:="Microsoft"
-$provider.permission:="signedIn"
-$provider.clientId:="xxx-xxx-xxx-xxx-c460fc"
-$provider.redirectURI:="http://127.0.0.1:50993/authorize/"
-$provider.scope:="https://graph.microsoft.com/.default"
-
-// Include the token from the previous request
-$provider.token:=$myCurrentToken
-
-// Re-create OAuth2 object with the stored token
-$OAuth:= cs.NetKit.OAuth2Provider.new ($provider)
-
-// getToken() checks if the token has expired
-// If the token is still valid, it returns the current token
-// If the token has expired, it automatically requests a new one
-// If a refresh token is present, the token is automatically renewed without user sign-in
-// If no refresh token is available, the user will need to sign in again
-$myCurrentToken:=$OAuth.getToken()
-
-```
-
-**Note**: Some servers, like Google, do not always return the refresh token during subsequent token requests. In such cases, you should remember to include the refresh token in the token object before saving it for future use.
-
-### OAuth2ProviderObject.getToken()
-
-**OAuth2ProviderObject.getToken()** : Object
-
-|Parameter|Type||Description|
-|---------|--- |------|------|
-|Result|Object|<-| Object that holds information on the token retrieved
-
-
-#### Description
-
-`.getToken()` returns an object that contains a `token` property (as defined by the [IETF](https://datatracker.ietf.org/doc/html/rfc6749#section-5.1)), as well as optional additional information returned by the server:
-
-Property|Object properties|Type|Description |
-|--- |---------| --- |------|
-|token||Object| Token returned |
-|| expires_in | Text | How long the access token is valid (in seconds). |
-|| access_token |Ttext | The requested access token. |
-|| refresh_token | Text | Your app can use this token to acquire additional access tokens after the current access token expires. Refresh tokens are long-lived, and can be used to retain access to resources for extended periods of time. Available only if the value of the `permission` property is "signedIn". |
-|| token_type | Text | Indicates the token type value. The only token type that Azure AD supports is "Bearer". |
-||scope|Text| A space separated list of the Microsoft Graph permissions that the access_token is valid for.|
-|tokenExpiration || Text | Timestamp (ISO 8601 UTC) that indicates the expiration time of the token|
-
-If the value of `token` is empty, the command sends a request for a new token.
-
-If the token has expired:
-* If the token object has the `refresh_token` property, the command sends a new request to refresh the token and returns it.
-* If the token object does not have the `refresh_token` property, the command automatically sends a request for a new token.
-
-When requesting access on behalf of a user ("signedIn" mode) the command opens a web browser to request authorization.
-
-In "signedIn" mode, when `.getToken()` is called, a web server included in 4D NetKit starts automatically on the port specified in the [redirectURI parameter](#description) to intercept the provider's authorization response and display it in the browser.
-
-## Office365
-
-The `Office365` class allows you to call the [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/overview#data-and-services-powering-the-microsoft-365-platform) to:
-* get information from Office365 applications, such as user information
-* create, move or send emails
-
-This can be done after a valid token request, (see [OAuth2Provider object](#oauth2provider)).
-
-The `Office365` class can be instantiated in two ways:
-* by calling the `New Office365 provider` method
-* by calling the `cs.NetKit.Office365.new()` function
-
-### **New Office365 provider**
-
-**New Office365 provider**( *paramObj* : Object { ; *options* : Object } ) : cs.NetKit.Office365
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|paramObj|cs.NetKit.OAuth2Provider|->| Object of the OAuth2Provider class |
-|options|Object|->| Additional options |
-|Result|cs.NetKit.Office365|<-| Object of the Office365 class|
-
-#### Description
-
-`New Office365 provider` instantiates an object of the `Office365` class.
-
-In `paramObj`, pass an [OAuth2Provider object](#new-auth2-provider).
-
-In `options`, you can pass an object that specifies the following options:
-
-|Property|Type|Description|
-|---------|---|------|
-|mailType|Text|Indicates the Mail type to use to send and receive emails. Possible types are:
- "MIME"
- "JMAP"
- "Microsoft" (default)|
-
-#### Returned object
-
-The returned `Office365` object contains the following properties:
-
-|Property||Type|Description|
-|----|-----|---|------|
-|mail||Object|Email handling object|
-||send()|Function|Sends the emails|
-||type|Text|(read-only) Mail type used to send and receive emails. Default is "Microsoft", can bet set using the `mailType` option|
-||userId|Text|User identifier, used to identify the user in Service mode. Can be the `id` or the `userPrincipalName`|
-
-
-#### Example 1
-
-To create the OAuth2 connection object and an Office365 object:
-
-```4d
-var $oAuth2: cs.NetKit.OAuth2Provider
-var $office365 : cs.NetKit.Office365
-
-$oAuth2:=New OAuth2 provider($param)
-$office365:=New Office365 provider($oAuth2;New object("mailType"; "Microsoft"))
-```
-
-#### Example 2
-
-Refer to [this tutorial](#authenticate-to-the-microsoft-graph-api-in-service-mode) for an example of connection in Service mode.
-
-### Office365.mail.append()
-
-**Office365.mail.append**( *email* : Object ; *folderId* : Text) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|email|Object|->| Microsoft message object to append|
-|folderId|Text|->| Id of the destination folder. Can be a folder id or a [Well-known folder name](#well-known-folder-name).|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.append()` creates a draft *email* in the *folderId* folder.
-
-In `email`, pass the email to create. It must be of the [Microsoft mail object](#microsoft-mail-object-properties) type.
-
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadWrite|
-|Application|Mail.ReadWrite|
-
-
-#### Example
-
-```4d
-$status:=$office365.mail.append($draft; $folder.id)
-```
-
-
-### Office365.mail.copy()
-
-**Office365.mail.copy**( *mailId* : Text ; *folderId* : Text) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailId|Text|->| Id of the mail to copy|
-|folderId|Text|->| Id of the destination folder. Can be a folder id or a [Well-known folder name](#well-known-folder-name).|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.copy()` copies the *mailId* email to the *folderId* folder within the user's mailbox.
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadWrite|
-|Application|Mail.ReadWrite|
-
-#### Example
-
-To copy an email from a folder to another:
-
-```4d
-$status:=$office365.mail.copy($mailId; $folderId)
-```
-
-
-### Office365.mail.createFolder()
-
-**Office365.mail.createFolder**( *name* : Text { ; *isHidden* : Boolean { ; *parentFolderId* : Text } }) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|name|Text|->|Display name of the new folder|
-|isHidden|Boolean|->|True to create a hidden folder (Default is False)|
-|parentFolderId|Text|->|ID of the parent folder to get. Can be a folder id or a [Well-known folder name](#well-known-folder-name).|
-|Result|Object|<-| [Status object](#status-object) |
-
-`Office365.mail.getFolder()` creates a new folder named *name* and returns its ID in the [status object](#status-object).
-
-By default, the new folder is not hidden. Pass `True` in the isHidden parameter to create a hidden folder. This property cannot be changed afterwards. Find more information in [Hidden mail folders](https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0#hidden-mail-folders) on the Microsoft web site.
-
-By default, the new folder is created at the root folder of the mailbox. If you want to create it within an existing folder, pass its id in the *parentFolderId* parameter.
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadWrite|
-|Application|Mail.ReadWrite|
-
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-#### Example
-
-You want to create a "Backup" mail folder at the root of your mailbox and move an email to this folder:
-
-```4d
-// Creates a new folder on the root
-$status:=$office365.mail.createFolder("Backup")
-If($status.success=True)
- $folderId:=$status.id
- // Moves your email in the new folder
- $status:=$office365.mail.move($mailId; $folderId)
-End if
-```
-
-### Office365.mail.delete()
-
-**Office365.mail.delete**( *mailId* : Text ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailId|Text|->| Id of the mail to delete|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.delete()` deletes the *mailId* email.
-
-
-#### Permissions
-
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadWrite|
-|Application|Mail.ReadWrite|
-
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-
-**Note:** You may not be able to delete items in the recoverable items deletions folder (for more information, see the [Microsoft's documentation website](https://learn.microsoft.com/en-us/graph/api/message-delete?view=graph-rest-1.0&tabs=http)).
-
-
-#### Example
-
-You want to delete all mails in the *$mails* collection:
-
-```4d
-For each($mail;$mails)
- $office365.mail.delete($mail.id)
-End for each
-```
-
-### Office365.mail.deleteFolder()
-
-**Office365.mail.deleteFolder**( *folderId* : Text ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|folderId|Text|->| ID of the folder to delete. Can be a folder id or a [Well-known folder name](#well-known-folder-name) if one exists.|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.deleteFolder()` deletes the *folderId* mail folder.
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadWrite|
-|Application|Mail.ReadWrite|
-
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-#### Example
-
-```4d
-$status:=$office365.mail.deleteFolder($folderId)
-```
-
-### Office365.mail.getFolder()
-
-**Office365.mail.getFolder**( *folderId* : Text ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|folderId|Text|->|ID of the folder to get. Can be a folder ID or a [Well-known folder name](#well-known-folder-name).|
-|Result|Object|<-|mailFolder object|
-
-`Office365.mail.getFolder()` allows you to get a **mailFolder** object from its *folderId*.
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
-|Application|Mail.ReadBasic.All, Mail.Read, Mail.ReadWrite|
-
-
-#### mailFolder object
-
-The method returns a **mailFolder** object containing the following properties (additional information can be returned by the server):
-
-| Property | Type | Description |
-|---|---|---|
-|childFolderCount|Integer|Number of immediate child mailFolders in the current mailFolder|
-|displayName|Text|mailFolder's display name|
-|id|Text|mailFolder's unique identifier|
-|isHidden|Boolean|Indicates whether the mailFolder is hidden. This property can be set only when creating the folder. Find more information in [Hidden mail folders](https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0#hidden-mail-folders) on the Microsoft web site.|
-|parentFolderId|Text|Unique identifier for the mailFolder's parent mailFolder.|
-|totalItemCount|Integer|Number of items in the mailFolder.|
-|unreadItemCount|Integer|Number of items in the mailFolder marked as unread.|
-
-
-### Office365.mail.getFolderList()
-
-**Office365.mail.getFolderList**( *options* : Object ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|options|Object|->| Description of folders to get|
-|Result|Object|<-| Status object that contains folder list and other information|
-
-`Office365.mail.getFolderList()` allows you to get a mail folder collection of the signed-in user.
-
-In *options*, pass an object to define the folders to get. The available properties for that object are (all properties are optional):
-
-| Property | Type | Description |
-|---|---|---|
-|folderId|text|Can be a folder id or a [Well-known folder name](#well-known-folder-name).
- If it is a parent folder id, get the folder collection under the specified folder (children folders
- If the property is omitted or its value is "", get the mail folder collection directly under the root folder.|
-|search|text|Restricts the results of a request to match a search criterion. The search syntax rules are available on [Microsoft's documentation website](https://docs.microsoft.com/en-us/graph/search-query-parameter#using-search-on-directory-object-collections).|
-|filter|text|Allows retrieving just a subset of folders. See [Microsoft's documentation on filter parameter](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter).|
-|select|text|Set of properties to retrieve. Each property must be separated by a comma (,). |
-|top|integer|Defines the page size for a request. Maximum value is 999. If `top` is not defined, default value is applied (10). When a result set spans multiple pages, you can use the `.next()` function to ask for the next page. See [Microsoft's documentation on paging](https://docs.microsoft.com/en-us/graph/paging) for more information. |
-|orderBy|text|Defines how returned items are ordered. Default is ascending order. Syntax: "fieldname asc" or "fieldname desc" (replace "fieldname" with the name of the field to be arranged).|
-|includeHiddenFolders|boolean|True to include hidden folders in the response. False (default) to not return hidden folders. |
-
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
-|Application|Mail.ReadBasic.All, Mail.Read, Mail.ReadWrite|
-
-
-#### Returned object
-
-The method returns a status object containing the following properties:
-
-| Property || Type | Description |
-|---|---| ---|---|
-| errors | | Collection | Collection of 4D error items (not returned if an Office 365 server response is received)|
-||[].errcode|Integer|4D error code number|
-||[].message|Text|Description of the 4D error|
-||[].componentSignature|Text|Signature of the internal component that returned the error|
-| isLastPage | | Boolean | `True` if the last page is reached |
-| page || Integer | Folder information page number. Starts at 1. By default, each page holds 10 results. Page size limit can be set in the `top` option. |
-| next() || Function | Function that updates the `folders` collection with the next mail information page and increases the `page` property by 1. Returns a boolean value:
- If a next page is successfully loaded, returns `True`
- If no next page is returned, the `folders` collection is not updated and `False` is returned |
-| previous() || Function | Function that updates the `folders` collection with the previous folder information page and decreases the `page` property by 1. Returns a boolean value:
- If a previous page is successfully loaded, returns `True`
- If no previous `page` is returned, the `folders` collection is not updated and `False` is returned |
-| statusText || Text | Status message returned by the Office 365 server, or last error returned in the 4D error stack |
-| success | | Boolean | `True` if the `Office365.mail.getFolderList()` call is successful, `False` otherwise |
-| folders || Collection | Collection of `mailFolder` objects with information on folders.|
-|| [].childFolderCount|Integer|The number of immediate child mailFolders in the current mailFolder.
-|| [].displayName| Text| The mailFolder's display name.
-|| [].id| Text| The mailFolder's unique identifier.
-|| [].isHidden| Boolean| Indicates whether the mailFolder is hidden. This property can be set only when creating the folder. Find more information in Hidden mail folders.
-|| [].parentFolderId| Text| The unique identifier for the mailFolder's parent mailFolder.
-|| [].totalItemCount |Integer| The number of items in the mailFolder.
-|| [].unreadItemCount| Integer |The number of items in the mailFolder marked as unread.
-
-
-The method returns an empty collection in the `folders` property if:
-- no folders are found at the defined location
-- an error is thrown
-
-#### Example
-
-You want to :
-
-```4d
-//get the mail folder collection under the root folder (in $result.folders)
-var $result : Object
-$result:=$office365.mail.getFolderList()
-
-//get the mail subfolder collection under the 9th folder
-var $subfolders : Collection
-$subfolders:=$office365.mail.getFolderList($result.folders[8].id)
-```
-
-
-### Office365.mail.getMail()
-
-**Office365.mail.getMail**( *mailId* : Text { ; *options* : Object } ) : Object
**Office365.mail.getMail**( *mailId* : Text { ; *options* : Object } ) : Blob
-
-#### Parameters
-
-|Parameter||Type||Description|
-|-----|----|--- |:---:|------|
-|mailId||Text|->| Id of the mail to get|
-|options||Object|->|Format options for the returned mail object|
-||mailType|Text|| Type of the mail object to return. Available values:
- "MIME"
- "JMAP"
- "Microsoft" (default)
By default if omitted, the same format as the [`mail.type` property](#new-office365-provider) is used|
-||contentType|Text|| Format of the `body` and `uniqueBody` properties to be returned. Available values:
- "text"
- "html" (default)|
-|Result||Blob | Object|<-| Downloaded mail|
-
-`Office365.mail.getMail()` allows you to get a single mail from its *mailId*.
-
-By default, the mail is returned with its original format, as defined in the [`mail.type` property of the provider](#new-office365-provider). However, you can convert it to any type using the `mailType` property of the *options* parameter.
-
-You can also select the preferred format of the `body` and `uniqueBody` properties of the returned mail using the `contentType` property of the *options* parameter.
-
-The data type of the function result depends on the mail type:
-
-|Mail type|Function result|
-|---|---|
-|MIME|Blob|
-|JMAP|Object|
-|Microsoft|Object|
-
-If an error occurs, the function returns Null and an error is generated.
-
-See also [Microsoft's documentation website](https://learn.microsoft.com/en-us/graph/api/message-get?view=graph-rest-1.0&tabs=http).
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadBasic, Mail.Read|
-|Delegated (personal Microsoft account)|Mail.ReadBasic, Mail.Read|
-|Application|Mail.ReadBasic.All, Mail.Read|
-
-
-#### Example
-
-Download a specific email:
-
-```4d
-$mail:=$office.mail.getMail($mailId)
-```
-
-
-### Office365.mail.getMails()
-
-**Office365.mail.getMails**( *options* : Object ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|options|Object|->| Description of mails to get|
-|Result|Object|<-| Status object that contains mail list and other information|
-
-`Office365.mail.getMails()` allows you to get messages in the signed-in user's mailbox (for detailed information, please refer to the [Microsoft's documentation website](https://learn.microsoft.com/en-us/graph/api/user-list-messages?view=graph-rest-1.0&tabs=http)).
-
-This method returns mail bodies in HTML format only.
-
-In *options*, pass an object to define the mails to get. The available properties for that object are (all properties are optional):
-
-| Property | Type | Description |
-|---|---|---|
-|folderId|text|To get messages in a specific folder. Can be a folder id or a [Well-known folder name](#well-known-folder-name). If the destination folder is not present or empty, get all the messages in a user's mailbox.|
-|search|text|Restricts the results of a request to match a search criterion. The search syntax rules are available on [Microsoft's documentation website](https://learn.microsoft.com/en-us/graph/search-query-parameter?tabs=http#using-search-on-message-collections).|
-|filter|text|Allows retrieving just a subset of mails. See [Microsoft's documentation on filter parameter](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter).|
-|select|text|Set of [properties of the Microsoft Mail object](#microsoft-mail-object-properties) to retrieve. Each property must be separated by a comma (,). |
-|top|integer|Defines the page size for a request. Maximum value is 999. If `top` is not defined, default value is applied (10). When a result set spans multiple pages, you can use the `.next()` function to ask for the next page. See [Microsoft's documentation on paging](https://docs.microsoft.com/en-us/graph/paging) for more information. |
-|orderBy|text|Defines how returned items are ordered. Default is ascending order. Syntax: "fieldname asc" or "fieldname desc" (replace "fieldname" with the name of the field to be arranged).|
-
-
-#### Returned object
-
-The method returns a status object containing the following properties:
-
-| Property || Type | Description |
-|---|---| ---|---|
-| errors | | Collection | Collection of 4D error items (not returned if an Office 365 server response is received)|
-||[].errcode|Integer|4D error code number|
-||[].message|Text|Description of the 4D error|
-||[].componentSignature|Text|Signature of the internal component that returned the error|
-| isLastPage | | Boolean | `True` if the last page is reached |
-| page || Integer | Mail information page number. Starts at 1. By default, each page holds 10 results. Page size limit can be set in the `top` option. |
-| next() || Function | Function that updates the `mails` collection with the next mail information page and increases the `page` property by 1. Returns a boolean value:
- If a next page is successfully loaded, returns `True`
- If no next page is returned, the `mails` collection is not updated and `False` is returned |
-| previous() || Function | Function that updates the `folders` collection with the previous mail information page and decreases the `page` property by 1. Returns a boolean value:
- If a previous page is successfully loaded, returns `True`
- If no previous `page` is returned, the `mails` collection is not updated and `False` is returned |
-| statusText || Text | Status message returned by the Office 365 server, or last error returned in the 4D error stack |
-| success | | Boolean | `True` if the `Office365.mail.getFolderList()` call is successful, `False` otherwise |
-| mails || Collection | Collection of [Microsoft mail objects](#microsoft-mail-object-properties). If no mail is returned, the collection is empty|
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
-|Application|Mail.ReadBasic.All, Mail.Read, Mail.ReadWrite|
-
-#### Example
-
-You want to retrieve *sender* and *subject* properties of all the mails present in the Inbox folder, using its [well-known folder name](#well-known-folder-name):
-
-```4d
-$param:=New object
-$param.folderId:="inbox"
-$param.select:="sender,subject"
-
-$mails:=$office365.mail.getMails($param)
-```
-
-### Office365.mail.move()
-
-**Office365.mail.move**( *mailId* : Text ; *folderId* : Text) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailId|Text|->| Id of the mail to move|
-|folderId|Text|->| Id of the destination folder. Can be a folder id or a [Well-known folder name](#well-known-folder-name).|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.move()` moves the *mailId* email to the *folderId* folder. It actually creates a new copy of the email in the destination folder and removes the original email from its source folder.
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadWrite|
-|Application|Mail.ReadWrite|
-
-
-#### Example
-
-To move an email from a folder to another:
-
-```4d
-$status:=$office365.mail.move($mailId; $folderId)
-```
-
-### Office365.mail.renameFolder()
-
-**Office365.mail.renameFolder**( *folderId* : Text ; *name* : Text ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|folderId|Text|->| ID of the folder to rename|
-|name|Text|->| New display name for the folder|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.renameFolder()` renames the *folderId* mail folder with the provided *name*.
-
-Note that the renamed folder ID is different from the *folderId*. You can get it in the returned [status object](#status-object).
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.ReadWrite|
-|Delegated (personal Microsoft account)|Mail.ReadWrite|
-|Application|Mail.ReadWrite|
-
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-#### Example
-
-You want to rename the the "Backup" folder to "Backup_old":
-
-```4d
-$status:=$office365.mail.renameFolder($folderId; "Backup_old")
-```
-
-
-### Office365.mail.reply()
-
-**Office365.mail.reply**( *reply* : Object ; *mailId* : Text { ; *replyAll* : Boolean } ) : Object
-
-#### Parameters
-
-|Parameter||Type||Description|
-|----|-----|--- |:---:|------|
-|reply||Object|->| reply object|
-||message|Text | Blob | Object|->|Microsoft message (object) or JMAP (object) or MIME (Blob / Text) that contains the reponse|
-||comment|Text|->| (only available with Microsoft message object or no message) Message used as body to reply to the email when present. You must specify either a *comment* or the [body property](#microsoft-mail-object-properties) of the message parameter; specifying both will return an HTTP 400 Bad Request error.|
-|mailId||Text|->| Id of the mail to which you reply|
-|replyAll||Boolean|->| True to reply to all recipients of the message. Default=False|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.reply()` replies to the sender of *mailId* message and, optionnally, to all recipients of the message.
-
-**Note:** Some mails, like drafts, cannot be replied.
-
-If you pass `False` in *replyAll* and if the original message specifies a recipient in the `replyTo` property, the reply is sent to the recipients in `replyTo` and not to the recipient in the `from` property.
-
-#### Returned object
-
-The method returns a [status object](#status-object)
-
-#### Permissions
-
-One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
-
-|Permission type|Permissions (from least to most privileged)
-|---|----|
-|Delegated (work or school account)|Mail.Send|
-|Delegated (personal Microsoft account)|Mail.Send|
-|Application|Mail.Send|
-
-#### Example
-
-To reply to an email and create a conversation:
-
-```4d
-$reply:=New object
-// Text that will be send as reply
-$reply.comment:="Thank you for your message"
-$status:=$office365.mail.reply($reply; $mails.mailId)
-```
-
-
-
-### Office365.mail.send()
-
-**Office365.mail.send**( *email* : Text ) : Object
**Office365.mail.send**( *email* : Object ) : Object
**Office365.mail.send**( *email* : Blob ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|email|Text | Blob | Object|->| Email to be sent|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.send()` sends an email using the MIME or JSON formats.
-
-In `email`, pass the email to be sent. Possible types:
-
-* Text or Blob: the email is sent using the MIME format
-* Object: the email is sent using the JSON format, in accordance with either:
- * the [Microsoft mail object properties](#microsoft-mail-object-properties)
- * the [4D email object format](https://developer.4d.com/docs/API/EmailObjectClass.html#email-object), which follows the JMAP specification
-
-The data type passed in `email` must be compatible with the [`Office365.mail.type` property](#returned-object-1). In the following example, since the mail type is `Microsoft`, `$email` must be an object. For the list of available properties, see [Microsoft mail object's properties](#microsoft-mail-object-properties):
-
-```4d
-$Office365:=New Office365 provider($token; New object("mailType"; "Microsoft"))
-$status:=$Office365.mail.send($email)
-```
-
-> To avoid authentication errors, make sure your application asks for permission to send emails through the Microsoft Graph API. See [Permissions](https://docs.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http#permissions).
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-### Office365.mail.update()
-
-**Office365.mail.update**( *mailId* : Text ; *updatedFields* : Object ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailId|Text|->|The ID of the email to update|
-|updatedFields|Object|->|email fields to update|
-|Result|Object|<-| [Status object](#status-object) |
-
-#### Description
-
-`Office365.mail.update()` allows you to update various properties of received or drafted emails.
-
-In *updatedFields*, you can pass several properties:
-
-|Property|Type|Description|Updatable only if isDraft = true|
-|:----|:----|:----|:----|
-|bccRecipients|Recipient|The Bcc recipients for the message.| |
-|body|ItemBody|The body of the message.|X|
-|categories|String collection|The categories associated with the message.| |
-|ccRecipients|Recipient collection|The Cc recipients for the message.| |
-|flag|followupFlag|The flag value that indicates the status, start date, due date, or completion date for the message.| |
-|from|Recipient|The mailbox owner and sender of the message. Must correspond to the actual mailbox used.|X|
-|importance|String|The importance of the message. The possible values are: Low, Normal, High.| |
-|inferenceClassification|String|The classification of the message for the user, based on inferred relevance or importance, or on an explicit override. The possible values are: focused or other.| |
-|internetMessageId|String|The message ID in the format specified by RFC2822.|X|
-|isDeliveryReceiptRequested|Boolean|Indicates whether a read receipt is requested for the message.|X|
-|isRead|Boolean|Indicates whether the message has been read.| |
-|isReadReceiptRequested|Boolean|Indicates whether a read receipt is requested for the message.|X|
-|replyTo|Recipient collection|The email addresses to use when replying.|X|
-|sender|Recipient|The account that is actually used to generate the message. Updatable when sending a message from a shared mailbox, or sending a message as a delegate. In any case, the value must correspond to the actual mailbox used.|X|
-|subject|String|The subject of the message.|X|
-|toRecipients|Recipient collection|The To recipients for the message.| |
-
-**Notes:**
-
-* Existing properties that are not included in the *updatedFields* object will maintain their previous values or be recalculated based on changes to other property values.
-* Specific properties, such as the body or subject, can only be updated for emails in draft status (isDraft = true).
-
-#### Returned object
-
-The method returns a [status object](#status-object).
-
-### Well-known folder names
-
-
-Outlook creates certain folders for users by default. Instead of using the corresponding `folder id` value, for convenience, you can use the well-known folder name when accessing these folders. Well-known names work regardless of the locale of the user's mailbox. For example, you can get the Drafts folder using its well-known name "draft". For more information, please refer to the [Microsoft Office documentation](https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0).
-
-
-### "Microsoft" mail object properties
-
-When you send an email with the "Microsoft" mail type, you must pass an object to `Office365.mail.send()`. For a comprehensive list of properties supported by Microsoft mail objects, please refer to the [Microsoft Office documentation](https://learn.microsoft.com/en-us/graph/api/resources/message?view=graph-rest-1.0#properties). Most common properties are listed below:
-
-| Property | Type | Description |
-|---|---|---|
-| attachments |[attachment](#attachment-object) collection | The attachments for the email. |
-| bccRecipients |[recipient](#recipient-object) collection | The Bcc: recipients for the message. |
-| body |itemBody object| The body of the message. It can be in HTML or text format.|
-| ccRecipients |[recipient](#recipient-object) collection | The Cc: recipients for the message. |
-| flag |[followup flag](#followup-flag-object) object| The flag value that indicates the status, start date, due date, or completion date for the message. |
-| from |[recipient](#recipient-object) object | The owner of the mailbox from which the message is sent. In most cases, this value is the same as the sender property, except for sharing or delegation scenarios. The value must correspond to the actual mailbox used.|
-| id |Text|Unique identifier for the message (note that this value may change if a message is moved or altered).|
-| importance|Text| The importance of the message. The possible values are: `low`, `normal`, and `high`.|
-| internetMessageHeaders |[internetMessageHeader](#internetmessageheader-object) collection | A collection of message headers defined by [RFC5322](https://www.ietf.org/rfc/rfc5322.txt). The set includes message headers indicating the network path taken by a message from the sender to the recipient.|
-| isDeliveryReceiptRequested |Boolean| Indicates whether a delivery receipt is requested for the message. |
-| isReadReceiptRequested |Boolean| Indicates whether a read receipt is requested for the message. |
-| replyTo |[recipient](#recipient-object) collection | The email addresses to use when replying. |
-| sender |[recipient](#recipient-object) object | The account that is actually used to generate the message. In most cases, this value is the same as the from property. You can set this property to a different value when sending a message from a shared mailbox, for a shared calendar, or as a delegate. In any case, the value must correspond to the actual mailbox used. Find out more about setting the [from and sender properties](https://docs.microsoft.com/en-us/graph/outlook-create-send-messages#setting-the-from-and-sender-properties) of a message. |
-| subject |Text| The subject of the message.|
-| toRecipients |[recipient](#recipient-object) collection | The To: recipients for the message. |
-
-#### Attachment object
-
-| Property | Type | Description |
-|---|---|---|
-|@odata.type|Text|always "#microsoft.graph.fileAttachment" (note that the property name requires that you use the `[""]` syntax)|
-|contentBytes|Text| The base64-encoded contents of the file (only to send mails) |
-|contentId| Text| The ID of the attachment in the Exchange store.|
-|contentType |Text| The content type of the attachment.|
-|id |Text|The attachment ID. (cid)|
-|isInline |Boolean |Set to true if this is an inline attachment.|
-|name| Text| The name representing the text that is displayed below the icon representing the embedded attachment.This does not need to be the actual file name.|
-|size|Number|The size in bytes of the attachment.|
-|getContent()|Function|Returns the contents of the attachment object in a `4D.Blob` object.|
-
-#### itemBody object
-
-| Property | Type | Description | Can be null of undefined |
-|---|---|---|---|
-|content|Text|The content of the item.|No|
-|contentType|Text| The type of the content. Possible values are `"text"` and `"html"` |No|
-
-#### recipient object
-
-| Property || Type | Description | Can be null of undefined |
-|---|---|---|---|---|
-|emailAddress||Object||Yes|
-||address|Text|The email address of the person or entity.|No|
-||name|Text| The display name of the person or entity.|Yes|
-
-#### internetMessageHeader object
-
-| Property | Type | Description | Can be null of undefined |
-|---|---|---|---|
-|name | Text|Represents the key in a key-value pair.|No|
-|value|Text|The value in a key-value pair.|No|
-
-#### followup flag object
-
-| Property | Type | Description |
-|---|---|---|
-|dueDateTime|[dateTime | TimeZone](#datetime-and-timezone)| The date and time that the follow up is to be finished. Note: To set the due date, you must also specify the `startDateTime`; otherwise, you will get a `400 Bad Request` response.|
-|flagStatus|Text|The status for follow-up for an item. Possible values are `"notFlagged"`, `"complete"`, and `"flagged"`.|
-|startDateTime|[dateTime | TimeZone](#datetime-and-timezone)| The date and time that the follow-up is to begin.|
-
-#### dateTime and TimeZone
-
-|Property|Type|Description|
-|---|---|---|
-|dateTime|Text|A single point of time in a combined date and time representation ({date}T{time}; for example, 2017-08-29T04:00:00.0000000).|
-|timeZone|Text|Represents a time zone, for example, "Pacific Standard Time". See below for more possible values.|
-
-In general, the timeZone property can be set to any of the time zones currently supported by Windows, as well as the additional time zones supported by the calendar API:
-* [Default time zones](https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones?view=windows-11)
-* [Additional time zones](https://docs.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0#additional-time-zones)
-
-
-
-#### Example: Create an email with a file attachment and send it
-
-Send an email with an attachment, on behalf of a Microsoft 365 user:
-
-```4d
-var $oAuth2 : cs.NetKit.OAuth2Provider
-var $token; $param; $email; $status : Object
-
-// Set up authentication
-$param:=New object()
-$param.name:="Microsoft"
-$param.permission:="signedIn"
-$param.clientId:="your-client-id" // Replace with your client ID
-$param.redirectURI:="http://127.0.0.1:50993/authorize/"
-// Ask permission to send emails on behalf of the Microsoft user
-$param.scope:="https://graph.microsoft.com/Mail.Send"
-
-$oAuth2:=New OAuth2 provider($param)
-
-$token:=$oAuth2.getToken()
-
-// Create the email, specify the sender and the recipient
-$email:=New object()
-$email.from:=New object("emailAddress"; New object("address"; "senderAddress@hotmail.fr")) // Replace with sender's email
-$email.toRecipients:=New collection(New object("emailAddress"; New object("address"; "recipientAddress@hotmail.fr")))
-$email.body:=New object()
-$email.body.content:="Hello, World!"
-$email.body.contentType:="html"
-$email.subject:="Hello, World!"
-
-// Create a text file and attach it to the email
-var $attachment : Object
-var $attachmentText : Text
-
-$attachmentText:="Simple text file"
-BASE64 ENCODE($attachmentText)
-$attachment:=New object
-$attachment["@odata.type"]:="#microsoft.graph.fileAttachment"
-$attachment.name:="attachment.txt"
-$attachment.contentBytes:=$attachmentText
-$email.attachments:=New collection($attachment)
-
-// Send the email
-$Office365:=New Office365 provider($token; New object("mailType"; "Microsoft"))
-$status:=$Office365.mail.send($email)
-```
-
-### Status object
-
-Several Office365.mail functions return a standard `**status object**`, containing the following properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|success|Boolean| True if the operation was successful|
-|statusText|Text| Status message returned by the server or last error returned by the 4D error stack|
-|errors|Collection| Collection of errors. Not returned if the server returns a `statusText`|
-|id|Text|
- [`copy()`](#office365-mail-copy) and [`move()`](#office365-mail-move): returned id of the mail.
- [`createFolder()`](#office365-mail-createFolder) and [`renameFolder()`](#office365-mail-renameFolder): returned id of the folder|
-
-
-Basically, you can test the `success` and `statusText` properties of this object to know if the function was correctly executed.
-
-### Error handling
-
-When an error occurs during the execution of an Office365.mail function:
-
-- if the function returns a [`**status object**`](#status-object), the error is handled by the status object and no error is thrown,
-- if the function does not return a **status object**, an error is thrown that you can intercept with a project method installed with `ON ERR CALL`.
-
-
-
-### Office365.user.get()
-
-**Office365.user.get**( *id* : Text { ; *select* : Text }) : Object
**Office365.user.get**( *userPrincipalName* : Text { ; *select* : Text }) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|id|Text|->| Unique identifier of the user to search for |
-|userPrincipalName|Text|->| User principal name (UPN) of the user to search for|
-|select|Text|->| Set of properties to be returned|
-|Result|Object|<-| Object holding information on the user|
-
-#### Description
-
-`Office365.user.get()` returns information on the user whose ID matches the *id* parameter, or whose User Principal Name (UPN) matches the *userPrincipalName* parameter.
-
-
-> The UPN is an Internet-style login name for the user based on the Internet standard RFC 822. By convention, it should correspond to the user's email name.
-
-If the ID or UPN is not found or connection fails, the command returns an object with `Null` as a value and throws an error.
-
-In *select*, you can pass a string that contains a specific set of properties you want to retrieve. Each property must be separated by a comma (,). If the select parameter is omitted, the function returns an object with a predefined set of properties (see below).
-
-> The list of available properties is available on [Microsoft's documentation website](https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0).
-
-#### Returned object
-
-By default, if the *select* parameter is omitted, the command returns an object with the following properties:
-
-| Property | Type | Description
-|---|---|---|
-id | Text | Unique identifier for the user
-businessPhones | Collection | The user's phone numbers.
-displayName | Text | Name displayed in the address book for the user.|
-givenName | Text | The user's first name.
-jobTitle | Text | The user's job title.
-mail | Text | The user's email address.
-mobilePhone | Text | The user's cellphone number.
-officeLocation | Text | The user's physical office location.
-preferredLanguage | Text | The user's language of preference.
-surname | Text | The user's last name.
-userPrincipalName | Text | The user's principal name.
-
-Otherwise, the object contains the properties specified in the `select` parameter.
-
-For more details on user information, see [Microsoft's documentation website](https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0).
-
-### Office365.user.getCurrent()
-
-**Office365.user.getCurrent**({*select* : Text}) : Object
-
-#### Description
-
-`Office365.user.getCurrent()` returns information on the current user. In this case, it requires a [signed-in user](https://docs.microsoft.com/en-us/graph/auth-v2-user), and therefore a delegated permission.
-
-The command returns a `Null` object if the session is not a sign-in session.
-
-In *select*, pass a string that contains a set of properties you want to retrieve. Each property must be separated by a comma (,).
-
-By default, if the *select* parameter is not defined, the command returns an object with a default set of properties (see the [property table](#returned-object)).
-
-#### Example
-
-To retrieve information from the current user:
-
-```4d
-var $userInfo; $params : Object
-var $oAuth2 : cs.NetKit.OAuth2Provider
-var $Office365 : cs.NetKit.Office365
-
-// Set up parameters:
-$params:=New object
-$params.name:="Microsoft"
-$params.permission:="signedIn"
-$params.clientId:="your-client-id" // Replace with your Microsoft identity platform client ID
-$params.redirectURI:="http://127.0.0.1:50993/authorize/"
-$param.scope:="https://graph.microsoft.com/.default"
-
-$oAuth2:=New Oauth2 provider($params) //Creates an OAuth2Provider Object
-
-$Office365:=New Office365 provider($oAuth2) // Creates an Office365 object
-
-// Return the properties specified in the parameter.
-$userInfo:=$Office365.user.getCurrent("id,userPrincipalName,\
-principalName,displayName,givenName,mail")
-```
-
-### Office365.user.list()
-
-**Office365.user.list**({*options*: Object}) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|options|Object|->| Additional options for the search|
-|result|Object|<-| Object holding a collection of users and additional info on the request|
-
-#### Description
-
-`Office365.user.list()` returns a list of Office365 users.
-
-In *options*, you can pass an object to specify additional search options. The following table groups the available search options:
-
-| Property | Type | Description |
-|---|---|---|
-| search | Text | Restricts the results of a request to match a search criterion. The search syntax rules are available on [Microsoft's documentation website](https://docs.microsoft.com/en-us/graph/search-query-parameter#using-search-on-directory-object-collections).|
-| filter | Text | Allows retrieving just a subset of users. See [Microsoft's documentation on filter parameter](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter). |
-| select | Text | Set of properties to retrieve. Each property must be separated by a comma (,). By default, if `select` is not defined, the returned user objects have a [default set of properties](#returned-object)|
-|top| Integer | Defines the page size for a request. Maximum value is 999. If `top` is not defined, the default value is applied (100). When a result set spans multiple pages, you can use the `.next()` function to ask for the next page. See [Microsoft's documentation on paging](https://docs.microsoft.com/en-us/graph/paging) for more information. |
-|orderBy| Text | Defines how the returned items are ordered. By default, they are arranged in ascending order. The syntax is "fieldname asc" or "fieldname desc". Replace "fieldname" with the name of the field to be arranged. |
-
-#### Returned object
-
-The returned object holds a collection of users as well as status properties and functions that allow you to navigate between different pages of results.
-
-By default, each user object in the collection has the [default set of properties listed in the `Office365.user.get()` function](#returned-object). This set of properties can be customized using the `select` parameter.
-
-| Property | Type | Description |
-|---|---|---|
-| errors | Collection | Collection of 4D error items (not returned if an Office 365 server response is received):
- [].errcode is the 4D error code number
- [].message is a description of the 4D error
- [].componentSignature is the signature of the internal component that returned the error|
-| isLastPage | Boolean | `True` if the last page is reached |
-| page | Integer | User information page number. Starts at 1. By default, each page holds 100 results. Page size limit can be set in the `top` option. |
-| next() | Function | Function that updates the `users` collection with the next user information page and increases the `page` property by 1. Returns a boolean value:
- If a next page is successfully loaded, returns `True`
- If no next page is returned, the `users` collection is not updated and `False` is returned |
-| previous() | Function | Function that updates the `users` collection with the previous user information page and decreases the `page` property by 1. Returns a boolean value:
- If a previous page is successfully loaded, returns `True`
- If no previous `page` is returned, the `users` collection is not updated and `False` is returned |
-| statusText | Text | Status message returned by the Office 365 server, or last error returned in the 4D error stack |
-| success | Boolean | `True` if the `Office365.user.list()` operation is successful, `False` otherwise |
-| users | Collection | Collection of objects with information on users.|
-
-
-#### Example
-
-```4d
-var $oAuth2 : cs.NetKit.OAuth2Provider
-var $Office365 : cs.NetKit.Office365
-var $userInfo; $params; $userList; $userList2; $userList3; $userList4 : Object
-var $col : Collection
-
-// Set up parameters:
-$params:=New object
-$params.name:="Microsoft"
-$params.permission:="signedIn"
-$params.clientId:="your-client-id" // Replace with your Microsoft identity platform client ID
-$params.redirectURI:="http://127.0.0.1:50993/authorize/"
-$params.scope:="https://graph.microsoft.com/.default"
-
-// Create an OAuth2Provider Object
-$oAuth2:=New OAuth2 provider($params)
-
-// Create an Office365 object
-$Office365:=New Office365 provider($oAuth2)
-
-// Return a list with the first 100 users
-$informationList1:=$Office365.user.list()
-
-// Return a list of users whose displayName is Jean
-$userList2:=$Office365.user.list(New object("filter"; "startswith(displayName,'Jean')"))
-
-// return a list of users whose display names contain "F" and arrange it in descending order.
-$userList3:=$Office365.user.list(New object("search"; "\"displayName:F\""; "orderBy"; "displayName desc"; "select"; "displayName"))
-
-// Create a list filled with all the userPrincipalName
-
-$userList4:=$Office365.user.list(New object("select"; "userPrincipalName"))
-$col:=New collection
-Repeat
- $col.combine($userList4.users)
-Until (Not($userList4.next()))
-```
-
-## Google
-
-The `Google` class allows you to send emails through the [Google REST API](https://developers.google.com/gmail/api/reference/rest/v1/users.messages).
-
-This can be done after a valid token request, (see [OAuth2Provider object](#oauth2provider)).
-
-The `Google` class is instantiated by calling the `cs.NetKit.Google.new()` function.
-
-
-### **cs.NetKit.Google.new()**
-
-**cs.NetKit.Google.new**( *oAuth2* : cs.NetKit.OAuth2Provider { ; *options* : Object } ) : cs.NetKit.Google
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|oAuth2|cs.NetKit.OAuth2Provider|->| Object of the OAuth2Provider class |
-|options|Object|->| Additional options |
-|Result|cs.NetKit.Google|<-| Object of the Google class|
-
-#### Description
-
-`cs.NetKit.Google.new()` instantiates an object of the `Google` class.
-
-In `oAuth2`, pass an [OAuth2Provider object](#oauth2provider).
-
-In `options`, you can pass an object that specifies the following options:
-
-|Property|Type|Description|
-|---------|---|------|
-|mailType|Text|Indicates the Mail type to use to send and receive emails. Possible types are:
- "MIME"
- "JMAP"|
-
-#### Returned object
-
-The returned `Google` object contains the following properties:
-
-|Property||Type|Description|
-|----|-----|---|------|
-|mail||Object|Email handling object|
-||[send()](#googlemailsend)|Function|Sends the emails|
-||type|Text|(read-only) Mail type used to send and receive emails. Can be set using the `mailType` option|
-||userId|Text|User identifier, used to identify the user in Service mode. Can be the `id` or the `userPrincipalName`|
-
-#### Example
-
-To create the OAuth2 connection object and a Google object:
-
-```4d
-var $oAuth2 : cs.NetKit.OAuth2Provider
-var $google : cs.NetKit.Google
-
-$oAuth2:=New OAuth2 provider($param)
-$google:=cs.NetKit.Google.new($oAuth2;New object("mailType"; "MIME"))
-```
-### Google.mail.append()
-
-**Google.mail.append**( *mail* : Text { ; *labelIds* : Collection } ) : Object
-**Google.mail.append**( *mail* : Blob { ; *labelIds* : Collection } ) : Object
-**Google.mail.append**( *mail* : Object { ; *labelIds* : Collection } ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mail|Text | Blob | Object|->|Email to be append |
-|labelIds|Collection|->|Collection of label IDs to add to messages. By default the DRAFT label is applied|
-|Result|Object|<-|[Status object](#status-object-google-class)|
-
-
-#### Description
-
-`Google.mail.append()` appends *mail* to the user's mailbox as a DRAFT or with designated *labelIds*.
-
->If the *labelIds* parameter is passed and the mail has a "from" or "sender" header, the Gmail server automatically adds the SENT label.
-
-#### Returned object
-
-The method returns a [**status object**](status-object-google-class) with an additional "id" property:
-
-|Property|Type|Description|
-|---------|--- |------|
-|id|Text|id of the email created on the server|
-|success|Boolean| [see Status object](#status-object-google-class)|
-|statusText|Text| [see Status object](#status-object-google-class)|
-|errors|Collection| [see Status object](#status-object-google-class)|
-
-#### Example
-
-To append an email :
-
-```4d
-$status:=$google.mail.append($mail)
-```
-
-By default, the mail is created with a DRAFT label. To change the designated label, pass a second parameter:
-
-```4d
-$status:=$google.mail.append($mail;["INBOX"])
-```
-
-### Google.mail.createLabel()
-
-**Google.mail.createLabel**( *labelInfo* : Object ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|[labelInfo](#labelinfo-object)|Object|->|Label information.|
-|Result|Object|<-|[Status object](#status-object-google-class)|
-
-#### Description
-
-`Google.mail.createLabel()` creates a new label.
-
-#### Returned object
-
-The method returns a [**status object**](status-object-google-class) with an additional "label" property:
-
-|Property|Type|Description|
-|---------|--- |------|
-|label|Object|contains a newly created instance of Label (see [labelInfo](#labelinfo-object))|
-|success|Boolean| [see Status object](#status-object-google-class)|
-|statusText|Text| [see Status object](#status-object-google-class)|
-|errors|Collection| [see Status object](#status-object-google-class)|
-
-#### Example
-
-To create a label named 'Backup':
-
-```4d
-$status:=$google.mail.createLabel({name: "Backup"})
-$labelId:=$status.label.id
-```
-
-### Google.mail.delete()
-
-**Google.mail.delete**( *mailID* : Text { ; *permanently* : Boolean } ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailID|Text|->|ID of the mail to delete |
-|permanently|Boolean|->|if permanently is true, deletes a message permanently. Otherwise, moves the specified message to the trash |
-|Result|Object|<-|[Status object](#status-object-google-class)|
-
-
-#### Description
-
-`Google.mail.delete()` deletes the specified message from the user's mailbox.
-
-#### Returned object
-
-The method returns a standard [**status object**](#status-object-google-class).
-
-#### Permissions
-
-This method requires one of the following OAuth scopes:
-
-```
-https://mail.google.com/
-https://www.googleapis.com/auth/gmail.modify
-```
-
-#### Example
-
-To delete an email permanently:
-
-```4d
-$status:=$google.mail.delete($mailId; True)
-```
-
-### Google.mail.deleteLabel()
-
-**Google.mail.deleteLabel**( *labelId* : Text ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|labelId|Text|->|The ID of the label|
-|Result|Object|<-|[Status object](#status-object-google-class)|
-
-#### Description
-
-`Google.mail.deleteLabel()` immediately and permanently deletes the specified label and removes it from any messages and threads that it is applied to.
-> This method is only available for labels with type="user".
-
-
-#### Returned object
-
-The method returns a standard [**status object**](#status-object-google-class).
-
-#### Example
-
-To delete a label:
-
-```4d
-$status:=$google.mail.deleteLabel($labelId)
-
-```
-
-### Google.mail.getLabel()
-
-**Google.mail.getLabel**( *labelId* : Text ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|labelId|Text|->|The ID of the label|
-|Result|Object|<-|[labelInfo](#labelinfo-object)|
-
-#### Description
-
-`Google.mail.getLabel()` returns the information of a label as a [labelInfo](#labelinfo-object) object.
-
-#### Returned object
-
-The returned [**labelInfo**](#labelinfo-object) object contains the following additional properties:
-
-
-|Property|Type|Description|
-|---------|---|------|
-|messagesTotal|Integer|The total number of messages with the label.|
-|messagesUnread|Integer|The number of unread messages with the label.|
-|threadsTotal|Integer|The total number of threads with the label.|
-|threadsUnread|Integer|The number of unread threads with the label.|
-
-#### Example
-
-To retrieve the label name, total message count, and unread messages:
-
-```4d
-$info:=$google.mail.getLabel($labelId)
-$name:=$info.name
-$emailNumber:=$info.messagesTotal
-$unread:=$info.messagesUnread
-```
-
-### Google.mail.getLabelList()
-
-**Google.mail.getLabelList**() : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|Result|Object|<-| Status object |
-
-#### Description
-
-`Google.mail.getLabelList()` returns an object containing the collection of all labels in the user's mailbox.
-
-
-#### Returned object
-
-The method returns a [**status object**](status-object-google-class) with an additional "labels" property:
-
-|Property|Type|Description|
-|---------|--- |------|
-|labels|Collection|Collection of [`mailLabel` objects](#maillabel-objects)|
-|success|Boolean| [see Status object](#status-object-google-class)|
-|statusText|Text| [see Status object](#status-object-google-class)|
-|errors|Collection| [see Status object](#status-object-google-class)|
-
-
-#### mailLabel object
-
-A `mailLabel` object contains the following properties (note that additional information can be returned by the server):
-
-|Property|Type|Description|
-|---------|--- |------|
-|name|Text|Display name of the label.|
-|id|Text|Immutable ID of the label.|
-|messageListVisibility|Text|Visibility of messages with this label in the message list in the Gmail web interface. Can be "show" or "hide"|
-|labelListVisibility|Text|Visibility of the label in the label list in the Gmail web interface. Can be:
- "labelShow": Show the label in the label list.
- "labelShowIfUnread": Show the label if there are any unread messages with that label
- "labelHide": Do not show the label in the label list.|
-|type|Text| Owner type for the label:
- "user": User labels are created by the user and can be modified and deleted by the user and can be applied to any message or thread.
- "system": System labels are internally created and cannot be added, modified, or deleted. System labels may be able to be applied to or removed from messages and threads under some circumstances but this is not guaranteed. For example, users can apply and remove the INBOX and UNREAD labels from messages and threads, but cannot apply or remove the DRAFTS or SENT labels from messages or threads.|
-
-
-### Google.mail.getMail()
-
-**Google.mail.getMail**( *mailID* : Text { ; *options* : Object } ) : Object
**Google.mail.getMail**( *mailID* : Text { ; *options* : Object } ) : Blob
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailID|Text|->|ID of the message to retrieve |
-|options|Object|->|Options |
-|Result|Object | Blob|<-| Downloaded mail|
-
-
-#### Description
-
-`Google.mail.getMail()` gets the specified message from the user's mailbox.
-
-In *options*, you can pass several properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|format|Text| The format to return the message in. Can be:
- "minimal": Returns only email message ID and labels; does not return the email headers, body, or payload. Returns a jmap object.
- "raw": Returns the full email message (default)
- "metadata": Returns only email message ID, labels, and email headers. Returns a jmap object.|
-|headers|Collection|Collection of strings containing the email headers to be returned. When given and format is "metadata", only include headers specified.|
-|mailType|Text|Only available if format is "raw". By default, the same as the *mailType* property of the mail (see [cs.NetKit.Google.new()](#csnetkitgooglenew)). If format="raw", the format can be:
- "MIME"
- "JMAP"|
-
-
-
-#### Returned object
-
-The method returns a mail in one of the following formats, depending on the `mailType`:
-
-|Format|Type|Comment|
-|---|---|---|
-|MIME|Blob||
-|JMAP|Object|Contains an `id` attribute|
-
-
-
-### Google.mail.getMailIds()
-
-**Google.mail.getMailIds**( { *options* : Object } ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|options|Object|->|Options for messages to get |
-|Result|Object|<-| Status object |
-
-#### Description
-
-`Google.mail.getMailIds()` returns an object containing a collection of message ids in the user's mailbox.
-
-In *options*, you can pass several properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|top|Integer|Maximum number of messages to return (default is 100). The maximum allowed value for this field is 500.|
-|search|Text| Only return messages matching the specified query. Supports the same query format as the Gmail search box. For example, "from:someuser@example.com rfc822msgid:somemsgid@example.com is:unread". See also [https://support.google.com/mail/answer/7190](https://support.google.com/mail/answer/7190).|
-|labelIds|Collection| Only return messages with labels that match all of the specified label IDs. Messages in a thread might have labels that other messages in the same thread don't have. To learn more, see [Manage labels on messages and threads](https://developers.google.com/gmail/api/guides/labels) in Google documentation. |
-|includeSpamTrash|Boolean|Include messages from SPAM and TRASH in the results. False by default. |
-
-
-
-#### Returned object
-
-The method returns a [**status object**](status-object-google-class) with additional properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|isLastPage|Boolean|True if the last page is reached|
-|page|Integer|Mail information page number. Starts at 1. By default, each page holds 10 results. Page size limit can be set in the `top` *option*.|
-|next()|`4D.Function` object|Function that updates the mail collection with the next mail information page and increases the `page` property by 1. Returns a boolean value:
- If a next page is successfully loaded, returns True
- If no next page is returned, the mail collection is not updated and False is returned.|
-|previous()|`4D.Function` object|Function that updates the mail collection with the previous mail information page and decreases the `page` property by 1. Returns a boolean value:
- If a previous page is successfully loaded, returns True
- If no previous page is returned, the mail collection is not updated and False is returned.|
-|mailIds|Collection| Collection of objects, where each object contains:
- *id* : Text : The id of the email
- *threadId* : Text : The id of the thread to which this Email belongs
- If no mail is returned, the collection is empty.|
-|success|Boolean| [see Status object](#status-object-google-class)|
-|statusText|Text| [see Status object](#status-object-google-class)|
-|errors|Collection| [see Status object](#status-object-google-class)|
-
-
-#### Permissions
-
-This method requires one of the following OAuth scopes:
-
-```
-https://www.googleapis.com/auth/gmail.modify
-https://www.googleapis.com/auth/gmail.readonly
-https://www.googleapis.com/auth/gmail.metadata
-```
-
-### Google.mail.getMails()
-
-**Google.mail.getMails**( *mailIDs* : Collection { ; *options* : Object } ) : Collection
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailIDs|Collection|->|Collection of strings (mail IDs), or a collection of objects (each object contains an ID property)|
-|options|Object|->|Options|
-|Result|Collection|<-|Collection of mails in format depending on *mailType*: JMAP (collection of objects) or MIME (collection of blobs)If no mail is returned, the collection is empty|
-
-
-#### Description
-
-`Google.mail.getMails()` gets a collection of emails based on the specified *mailIDs* collection.
-
-> The maximum number of IDs supported is 100. In order to get more than 100 mails, it's necessary to call the function multiple times; otherwise, the `Google.mail.getMails()` function returns null and throws an error.
-
-In *options*, you can pass several properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|format|Text| The format to return the message in. Can be:
- "minimal": Returns only email message ID and labels; does not return the email headers, body, or payload. Returns a jmap object.
- "raw": Returns the full email message (default)
- "metadata": Returns only email message ID, labels, and email headers. Returns a jmap object.|
-|headers|Collection|Collection of strings containing the email headers to be returned. When given and format is "metadata", only include headers specified.|
-|mailType|Text|Only available if format is "raw". By default, the same as the *mailType* property of the mail (see [cs.NetKit.Google.new()](#csnetkitgooglenew)). If format="raw", the format can be:
- "MIME"
- "JMAP"(Default)|
-
-
-
-#### Returned value
-
-The method returns a collection of mails in one of the following formats, depending on the `mailType`:
-
-|Format|Type|Comment|
-|---|---|---|
-|MIME|Blob||
-|JMAP|Object|Contains an `id` attribute|
-
-
-
-### Google.mail.send()
-
-**Google.mail.send**( *email* : Text ) : Object
**Google.mail.send**( *email* : Object ) : Object
**Google.mail.send**( *email* : Blob ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|email|Text | Blob | Object|->| Email to be sent|
-|Result|Object|<-| [Status object](#status-object-google-class) |
-
-#### Description
-
-`Google.mail.send()` sends an email using the MIME or JMAP formats.
-
-In `email`, pass the email to be sent. Possible types:
-
-* Text or Blob: the email is sent using the MIME format
-* Object: the email is sent using the JSON format, in accordance with the [4D email object format](https://developer.4d.com/docs/API/EmailObjectClass.html#email-object), which follows the JMAP specification.
-
-The data type passed in `email` must be compatible with the [`Google.mail.type` property](#returned-object-2). In the following example, since the mail type is `JMAP`, `$email` must be an object:
-
-```4d
-$Google:=cs.NetKit.Google.new($token;{mailType:"JMAP"})
-$status:=$Google.mail.send($email)
-```
-
-> To avoid authentication errors, make sure your application has appropriate authorizations to send emails. One of the following OAuth scopes is required: [modify](https://www.googleapis.com/auth/gmail.modify), [compose](https://www.googleapis.com/auth/gmail.compose), or [send](https://www.googleapis.com/auth/gmail.send). For more information, see the [Authorization guide](https://developers.google.com/workspace/guides/configure-oauth-consent).
-
-#### Returned object
-
-The method returns a standard [**status object**](#status-object-google-class).
-
-### Google.mail.untrash()
-
-**Google.mail.untrash**( *mailID* : Text ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailID|Text|->|The ID of the message to remove from Trash |
-|Result|Object|<-|[Status object](#status-object-google-class)|
-
-
-#### Description
-
-`Google.mail.untrash()` removes the specified message from the trash.
-
-#### Returned object
-
-The method returns a standard [**status object**](#status-object-google-class).
-
-#### Permissions
-
-This method requires one of the following OAuth scopes:
-
-```
-https://mail.google.com/
-https://www.googleapis.com/auth/gmail.modify
-```
-
-
-### Google.mail.update()
-
-**Google.mail.update**( *mailIDs* : Collection ; *options* : Object) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|mailIDs|Collection|->|Collection of strings (mail IDs), or collection of objects (each object contains an ID property)|
-|options|Object|->|Options|
-|Result|Object|<-| [Status object](#status-object-google-class) |
-
-> There is a limit of 1000 IDs per request.
-
-#### Description
-
-`Google.mail.update()` adds or removes labels on the specified messages to help categorizing emails. The label can be a system label (e.g., NBOX, SPAM, TRASH, UNREAD, STARRED, IMPORTANT) or a custom label. Multiple labels could be applied simultaneously.
-
-For more information check out the [label management documentation](https://developers.google.com/gmail/api/guides/labels).
-
-In *options*, you can pass the following two properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|addLabelIds|Collection|A collection of label IDs to add to messages.|
-|removeLabelIds|Collection|A collection of label IDs to remove from messages.|
-
-
-#### Returned object
-
-The method returns a standard [**status object**](#status-object-google-class).
-
-
-#### Example
-
-To mark a collection of emails as "unread":
-
-```4d
-$result:=$google.mail.update($mailIds; {addLabelIds: ["UNREAD"]})
-```
-
-### Google.mail.updateLabel()
-
-**Google.mail.updateLabel**( *labelId* : Text ; *labelInfo* : Object ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|labelId|Text|->|The ID of the label|
-|[labelInfo](#labelinfo-object)|Object|->|Label information to update|
-|Result|Object|<-|[Status object](#status-object-google-class)|
-
-#### Description
-
-`Google.mail.updateLabel()` updates the specified label.
-> This method is only available for labels with type="user".
-
-#### Returned object
-
-The method returns a [**status object**](status-object-google-class) with an additional "label" property:
-
-|Property|Type|Description|
-|---------|--- |------|
-|label|Object|contains an instance of Label (see [labelInfo](#labelinfo-object))|
-|success|Boolean| [see Status object](#status-object-google-class)|
-|statusText|Text| [see Status object](#status-object-google-class)|
-|errors|Collection| [see Status object](#status-object-google-class)|
-
-#### Example
-
-To update a previously created label to 'Backup January':
-
-```4d
-$status:=$google.mail.updateLabel($labelId; {name:"Backup January"})
-
-```
-
-
-### Google.user.get()
-
-**Google.user.get**( *id* : Text {; *select* : Text } ) : Object
-**Google.user.get**( *id* : Text {; *select* : Collection } ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|id|Text|->|The *resourceName* of the person to provide information about. Use the *resourceName* field returned by [Google.user.list()](#googleuserlist) to specify the person.|
-|select|Text \| Collection|->|Text: A comma-separated list of specific fields that you want to retrieve from each person (e.g., "names, phoneNumbers").
Collection: Collection of the specific fields.|
-|Result|Object|<-|Represents user's details, like names, emails, and phone numbers based on the selected fields.|
-
-#### Description
-
-`Google.user.get()` provides information about a [user](https://developers.google.com/people/api/rest/v1/people#Person) based on the *resourceName* provided in `id` and fields optionally specified in `select`.
-
-Supported fields include *addresses*, *ageRanges*, *biographies*, *birthdays*, *calendarUrls*, *clientData*, *coverPhotos*, *emailAddresses*, *events*, *externalIds*, *genders*, *imClients*, *interests*, *locales*, *locations*, *memberships*, *metadata*, *miscKeywords*, *names*, *nicknames*, *occupations*, *organizations*, *phoneNumbers*, *photos*, *relations*, *sipAddresses*, *skills*, *urls*, *userDefined*.
-
-
-#### Returned object
-
-The returned [user object](https://developers.google.com/people/api/rest/v1/people#Person) contains values for the specified field(s).
-
-If no fields have been specified in `select`, `Google.user.get()` returns *emailAddresses* and *names*. Otherwise, it returns only the specified field(s).
-
-#### Permissions
-
-No authorization required to access public data. For private data, one of the following OAuth scopes is required:
-
-https://www.googleapis.com/auth/contacts
-https://www.googleapis.com/auth/contacts.readonly
-https://www.googleapis.com/auth/contacts.other.readonly
-https://www.googleapis.com/auth/directory.readonly
-https://www.googleapis.com/auth/profile.agerange.read
-https://www.googleapis.com/auth/profile.emails.read
-https://www.googleapis.com/auth/profile.language.read
-https://www.googleapis.com/auth/user.addresses.read
-https://www.googleapis.com/auth/user.birthday.read
-https://www.googleapis.com/auth/user.emails.read
-https://www.googleapis.com/auth/user.gender.read
-https://www.googleapis.com/auth/user.organization.read
-https://www.googleapis.com/auth/user.phonenumbers.read
-https://www.googleapis.com/auth/userinfo.email
-https://www.googleapis.com/auth/userinfo.profile
-https://www.googleapis.com/auth/profile.language.read
-
-### Google.user.getCurrent()
-
-**Google.user.getCurrent**( { *select* : Text } ) : Object
-**Google.user.getCurrent**( { *select* : Collection } ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|select|Text \| Collection|->|Text: A comma-separated list of specific fields that you want to retrieve from each person (e.g., "names, phoneNumbers").
Collection: Collection of the specific fields.|
-|Result|Object|<-|Represents user's details, like names, emails, and phone numbers based on the selected fields.|
-
-#### Description
-
-`Google.user.getCurrent()` provides information about the authenticated [user](https://developers.google.com/people/api/rest/v1/people#Person) based on fields specified in `select`.
-
-Supported fields include *addresses*, *ageRanges*, *biographies*, *birthdays*, *calendarUrls*, *clientData*, *coverPhotos*, *emailAddresses*, *events*, *externalIds*, *genders*, *imClients*, *interests*, *locales*, *locations*, *memberships*, *metadata*, *miscKeywords*, *names*, *nicknames*, *occupations*, *organizations*, *phoneNumbers*, *photos*, *relations*, *sipAddresses*, *skills*, *urls*, *userDefined*.
-
-#### Returned object
-
-The returned [user object](https://developers.google.com/people/api/rest/v1/people#Person) contains values for the specific field(s).
-
-If no fields have been specified in `select`, `Google.user.getCurrent()` returns *emailAddresses* and *names*. Otherwise, it returns only the specified field(s).
-
-#### Permissions
-
-Requires the same OAuth scope package as [Google.user.get()](#permissions-15).
-
-#### Example
-
-To retrieve information from the current user:
-
-```4d
-var $google : cs.NetKit.Google
-var $oauth2 : cs.NetKit.OAuth2Provider
-var $param : Object
-
-// Set up parameters:
-$param:={}
-$param.name:="google"
-$param.permission:="signedIn"
-$param.clientId:="your-client-id" // Replace with your Google identity platform client ID
-$param.clientSecret:="xxxxxxxxx"
-$param.redirectURI:="http://127.0.0.1:50993/authorize/"
-$param.scope:=[]
-$param.scope.push("https://mail.google.com/")
-
-$param.scope.push("https://www.googleapis.com/auth/contacts")
-$param.scope.push("https://www.googleapis.com/auth/contacts.other.readonly")
-$param.scope.push("https://www.googleapis.com/auth/contacts.readonly")
-$param.scope.push("https://www.googleapis.com/auth/directory.readonly")
-$param.scope.push("https://www.googleapis.com/auth/user.addresses.read")
-$param.scope.push("https://www.googleapis.com/auth/user.birthday.read")
-$param.scope.push("https://www.googleapis.com/auth/user.emails.read")
-$param.scope.push("https://www.googleapis.com/auth/user.gender.read")
-$param.scope.push("https://www.googleapis.com/auth/user.organization.read")
-$param.scope.push("https://www.googleapis.com/auth/user.phonenumbers.read")
-$param.scope.push("https://www.googleapis.com/auth/userinfo.email")
-$param.scope.push("https://www.googleapis.com/auth/userinfo.profile")
-
-
-$oauth2:=New OAuth2 provider($param)
-
-$google:=cs.NetKit.Google.new($oauth2)
-
-var $currentUser1:=$google.user.getCurrent()
-//without parameters, returns by default "emailAddresses" and "names"
-
-var $currentUser2:=$google.user.getCurrent("phoneNumbers")
-//returns the field "phoneNumbers"
-```
-
-### Google.user.list()
-
-**Google.user.list**( { *options* : Object } ) : Object
-
-#### Parameters
-
-|Parameter|Type||Description|
-|---------|--- |:---:|------|
-|options|Object|->|A set of options defining how to retrieve and filter user data|
-|Result|Object|<-|An object containing a structured collection of [user](https://developers.google.com/people/api/rest/v1/people#Person) data organized into pages|
-
-#### Description
-
-`Google.user.list()` provides a list of domain profiles or domain contacts in the authenticated user's domain directory.
-
-> If the contact sharing or the External Directory sharing is not allowed in the Google admin, the returned `users` collection is empty.
-
-In *options*, you can pass the following properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|select|Text \| Collection|Text: A comma-separated list of specific fields that you want to retrieve from each person (e.g., "names, phoneNumbers").
Collection: Collection of the specific fields.
If omitted, defaults to returning emailAddresses and names.|
-|sources|Text \| Collection|Specifies the directory source to return. Values:
- DIRECTORY_SOURCE_TYPE_UNSPECIFIED (Unspecified),
- DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT (Google Workspace domain shared contact),
- DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE (default, Workspace domain profile).|
-|mergeSources|Text \| Collection|Adds related data if linked by verified join keys such as email addresses or phone numbers.
- DIRECTORY_MERGE_SOURCE_TYPE_UNSPECIFIED (Unspecified),
- DIRECTORY_MERGE_SOURCE_TYPE_CONTACT (User owned contact).|
-|top|Integer|Sets the maximum number of people to retrieve per page, between 1 and 1000 (default is 100).|
-
-#### Returned object
-
-The returned object holds a collection of [users objects](https://developers.google.com/people/api/rest/v1/people#Person) as well as [**status object**](status-object-google-class) properties and functions that allow you to navigate between different pages of results.
-
-|Property|Type|Description|
-|---------|--- |------|
-|users|Collection|A collection of [user objects](https://developers.google.com/people/api/rest/v1/people#Person), each containing detailed information about individual users|
-|isLastPage|Boolean|Indicates whether the current page is the last one in the collection of user data.|
-|page|Integer|Represents the current page number of user information, starting from 1. By default, each page contains 100 results, but the page size limit can be adjusted using the *top* option.|
-|next()|Function|A function that retrieves the next page of user information. Returns True if successful; otherwise, returns False if there is no next page and the users collection is not updated.|
-|previous()|Function|A function that retrieves the previous page of user information. Returns True if successful; otherwise, returns False if there is no previous page and the users collection is not updated.|
-|success|Boolean| [see Status object](#status-object-google-class)|
-|statusText|Text| [see Status object](#status-object-google-class)|
-|errors|Collection| [see Status object](#status-object-google-class)|
-
-#### Permissions
-
-Requires the same OAuth scope package as [Google.user.get()](#permissions-15).
-
-#### Example
-
-To retrieve user data in a structured collection organized into pages with a maximum of `top` users per page:
-
-```4d
-var $google : cs.NetKit.Google
-var $oauth2 : cs.NetKit.OAuth2Provider
-var $param : Object
-
-$param:={}
-$param.name:="google"
-$param.permission:="signedIn"
-$param.clientId:="your-client-id" // Replace with your Google identity platform client ID
-$param.clientSecret:="xxxxxxxxx"
-$param.redirectURI:="http://127.0.0.1:50993/authorize/"
-$param.scope:=[]
-$param.scope.push("https://mail.google.com/")
-
-$param.scope.push("https://www.googleapis.com/auth/contacts")
-$param.scope.push("https://www.googleapis.com/auth/contacts.other.readonly")
-$param.scope.push("https://www.googleapis.com/auth/contacts.readonly")
-$param.scope.push("https://www.googleapis.com/auth/directory.readonly")
-$param.scope.push("https://www.googleapis.com/auth/user.addresses.read")
-$param.scope.push("https://www.googleapis.com/auth/user.birthday.read")
-$param.scope.push("https://www.googleapis.com/auth/user.emails.read")
-$param.scope.push("https://www.googleapis.com/auth/user.gender.read")
-$param.scope.push("https://www.googleapis.com/auth/user.organization.read")
-$param.scope.push("https://www.googleapis.com/auth/user.phonenumbers.read")
-$param.scope.push("https://www.googleapis.com/auth/userinfo.email")
-$param.scope.push("https://www.googleapis.com/auth/userinfo.profile")
-
-
-$oauth2:=New OAuth2 provider($param)
-
-$google:=cs.NetKit.Google.new($oauth2)
-
-var $userList:=$google.user.list({top:10})
-```
-
-
-### labelInfo object
-
-Several Google.mail label management methods use a `labelInfo` object, containing the following properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|id|Text|The ID of the label.|
-|name|Text|The display name of the label. (mandatory)|
-|messageListVisibility|Text|The visibility of messages with this label in the message list.
Can be:
- "show": Show the label in the message list. <
- "hide": Do not show the label in the message list. |
-|labelListVisibility|Text|The visibility of the label in the label list.
Can be:
- "labelShow": Show the label in the label list.
- "labelShowIfUnread" : Show the label if there are any unread messages with that label.
- "labelHide": Do not show the label in the label list. |
-|[color](https://developers.google.com/gmail/api/reference/rest/v1/users.labels?hl=en#color)|Object|The color to assign to the label (color is only available for labels that have their type set to user).
The color object has 2 attributes :
- textColor: text: The text color of the label, represented as hex string. This field is required in order to set the color of a label.
- backgroundColor: text: The background color represented as hex string #RRGGBB (ex for black: #000000). This field is required in order to set the color of a label. |
-|type|Text|The owner type for the label.
Can be:
- "system": Labels created by Gmail.
- "user": Custom labels created by the user or application.
System labels are internally created and cannot be added, modified, or deleted. They're may be able to be applied to or removed from messages and threads under some circumstances but this is not guaranteed. For example, users can apply and remove the INBOX and UNREAD labels from messages and threads, but cannot apply or remove the DRAFTS or SENT labels from messages or threads. User labels are created by the user and can be modified and deleted by the user and can be applied to any message or thread. |
-
-
-### Status object (Google class)
-
-Several Google.mail functions return a `status object`, containing the following properties:
-
-|Property|Type|Description|
-|---------|--- |------|
-|success|Boolean| True if the operation was successful|
-|statusText|Text| Status message returned by the Gmail server or last error returned by the 4D error stack|
-|errors | Collection | Collection of 4D error items (not returned if a Gmail server response is received):
- [].errcode is the 4D error code number
- [].message is a description of the 4D error
- [].componentSignature is the signature of the internal component that returned the error|
-
-Basically, you can test the `success` and `statusText` properties of this object to know if the function was correctly executed.
-
-Some functions adds specific properties to the **status object**, properties are described with the functions.
-
-
-## Tutorials
-
-### Authenticate to the Microsoft Graph API in service mode
-
-#### Objectives
-
-Establish a connection to the Microsoft Graph API in service mode.
-
-#### Prerequisites
-
-* You have registered an application with the [Microsoft identity platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) and obtained your application ID (also called client ID) and client secret.
-
-> Here, the term "application" does not refer to an application built in 4D. It refers to an entry point you create on the Azure portal. You use the generated client ID to tell your 4D application to trust the Microsoft identity platform.
-
-#### Steps
-
-Once you have your client ID and client secret, you're ready to establish a connection to your Azure application.
-
-1. Open your 4D application, create a method and insert the following code:
-
-```4d
-var $oAuth2 : cs.NetKit.OAuth2Provider
-var $office365 : cs.NetKit.Office365
-
-var $credential:={}
-$credential.name:="Microsoft"
-$credential.permission:="service"
-
-$credential.clientId:="your-client-id" //Replace with your Microsoft identity platform client ID
-$credential.clientSecret:="your-client-secret" //Replace with your client secret
-$credential.tenant:="your-tenant-id" // Replace with your tenant ID
-$credential.scope:="https://graph.microsoft.com/.default"
-
-$oAuth2:=New OAuth2 provider($credential)
-
-$office365:=$cs.NetKit.Office365.new($oAuth2; {mailType: "MIME"})
-// In service mode, you need to indicate on behalf of which user you are sending the request:
-$office365.mail.userId:="MyUserPrincipalName"
-// Get mails of MyUserPrincipalName account
-$mailList:=$office365.mail.getMails()
-
-```
-
-2. Execute the method to establish the connection.
-
-### (Archived) Authenticate to the Microsoft Graph API in signedIn mode and send an email with SMTP
-
-> This tutorial has been archived. We recommend using the [Office365.mail.send()](#office365providermailsend) method to send emails.
-#### Objectives
-
-Establish a connection to the Microsoft Graph API in signedIn mode, and send an email using the [SMTP Transporter class](http://developer.4d.com/docs/fr/API/SMTPTransporterClass.html).
-
-In this example, we get access [on behalf of a user](https://docs.microsoft.com/en-us/graph/auth-v2-user).
-
-#### Prerequisites
-
-* You have registered an application with the [Microsoft identity platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) and obtained your application ID (also called client ID).
-
-> Here, the term "application" does not refer to an application built in 4D. It refers to an entry point you create on the Azure portal. You use the generated client ID to tell your 4D application to trust the Microsoft identity platform.
-
-* You have a Microsoft e-mail account. For example, you signed up for an e-mail account with Microsoft's webmail services designated domains (@hotmail.com, @outlook.com, etc.).
-
-#### Steps
-
-Once you have your client ID, you're ready to establish a connection to your Azure application and send an email:
-
-1. Open your 4D application, create a method and insert the following code:
-
-```4d
-var $token; $param; $email : Object
-var $oAuth2 : cs.NetKit.OAuth2Provider
-var $address : Text
-
-// Configure authentication
-
-
-$param:=New object
-$param.name:="Microsoft"
-$param.permission:="signedIn"
-$param.clientId:="your-client-id" // Replace with your Microsoft identity platform client ID
-$param.redirectURI:="http://127.0.0.1:50993/authorize/"
-$param.scope:="https://outlook.office.com/SMTP.Send" // Get consent for sending an smtp email
-
-// Instantiate an object of the OAuth2Provider class
-$oAuth2:=New OAuth2 provider($param)
-
-// Request a token using the class function
-
-// Send a token request and start the a web server on the port specified in $param.redirectURI
-//to intercept the authorization response
-$token:=$oAuth2.getToken()
-
-// Set the email address for SMTP configuration
-$address:= "email-sender-address@outlook.fr" // Replace with your Microsoft email account address
-
-// Set the email's content and metadata
-$email:=New object
-$email.subject:="My first mail"
-$email.from:=$address
-$email.to:="email-recipient-address@outlook.fr" // Replace with the recipient's email address
-$email.textBody:="Test mail \r\n This is just a test e-mail \r\n Please ignore it"
-
-// Configure the SMTP connection
-$parameters:=New object
-$parameters.accessTokenOAuth2:=$token
-
-$parameters.authenticationMode:=SMTP authentication OAUTH2
-$parameters.host:="smtp.office365.com"
-$parameters.user:=$address
-
-// Send the email
-
-$smtp:=SMTP New transporter($parameters)
-$statusSend:=$smtp.send($email)
-
-```
-
-2. Execute the method. Your browser opens a page that allows you to authenticate.
-
-3. Log in to your Microsoft Outlook account and check that you've received the email.
-
+* [OAuth2Provider class](./docs/OAuth2Provider.md)
+* [Office365 class](./docs/Office365.md)
+* [Google class](./docs/Google.md)
+* [Tutorial : Authenticate to the Microsoft Graph API in service mode](./docs/Tutorial.md)
## Copyrights
diff --git a/Documentation/Assets/authorization.png b/docs/Assets/authorization.png
similarity index 100%
rename from Documentation/Assets/authorization.png
rename to docs/Assets/authorization.png
diff --git a/docs/Google.md b/docs/Google.md
new file mode 100644
index 0000000..1890b6b
--- /dev/null
+++ b/docs/Google.md
@@ -0,0 +1,1361 @@
+# Google Class
+
+## Overview
+
+The `Google` class allows you to send emails through the [Google REST API](https://developers.google.com/gmail/api/reference/rest/v1/users.messages).
+
+This can be done after a valid token request, (see [OAuth2Provider object](#oauth2provider)).
+
+The `Google` class is instantiated by calling the `cs.NetKit.Google.new()` function.
+
+**Warning:** Shared objects are not supported by the 4D NetKit API.
+
+
+## Table of Contents
+
+### [Initialization](#csnetkitgooglenew)
+
+* [cs.NetKit.Google.new()](#csnetkitgooglenew)
+
+### [Calendar](#calendar-1)
+
+* [Google.Calendar.getCalendar()](#googlecalendargetcalendar)
+* [Google.Calendar.getCalendars()](#googlecalendargetcalendars)
+* [Google.Calendar.getEvent()](#googlecalendargetevent)
+* [Google.Calendar.getEvents()](#googlecalendargetevents)
+* [Google.Calendar.createEvent()](#googlecalendarcreateevent)
+* [Google.Calendar.updateEvent()](#googlecalendarupdateevent)
+* [Google.Calendar.deleteEvent()](#googlecalendardeleteevent)
+* [Event object](#event-object)
+
+### [Mail](#mail-1)
+
+* [Google.mail.send()](#googlemailsend)
+* [Google.mail.append()](#googlemailappend)
+* [Google.mail.update()](#googlemailupdate)
+* [Google.mail.createLabel()](#googlemailcreatelabel)
+* [Google.mail.updateLabel()](#googlemailupdatelabel)
+* [Google.mail.deleteLabel()](#googlemaildeletelabel)
+* [Google.mail.delete()](#googlemaildelete)
+* [Google.mail.getLabel()](#googlemailgetlabel)
+* [Google.mail.getLabelList()](#googlemailgetlabellist)
+* [Google.mail.getMail()](#googlemailgetmail)
+* [Google.mail.getMailIds()](#googlemailgetmailids)
+* [Google.mail.getMails()](#googlemailgetmails)
+* [Google.mail.untrash()](#googlemailuntrash)
+* [labelInfo object](#labelinfo-object)
+
+### [User](#user-1)
+
+* [Google.user.get()](#googleuserget)
+* [Google.user.getCurrent()](#googleusergetcurrent)
+* [Google.user.list()](#googleuserlist)
+
+### [Status](#status-object)
+
+* [Status object](#status-object)
+
+## **cs.NetKit.Google.new()**
+
+**cs.NetKit.Google.new**( *oAuth2* : cs.NetKit.OAuth2Provider { ; *param* : Object } ) : cs.NetKit.Google
+
+### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|oAuth2|cs.NetKit.OAuth2Provider|->| Object of the OAuth2Provider class |
+|param|Object|->| Additional options |
+|Result|cs.NetKit.Google|<-| Object of the Google class|
+
+### Description
+
+`cs.NetKit.Google.new()` instantiates an object of the `Google` class.
+
+In `oAuth2`, pass an [OAuth2Provider object](#oauth2provider).
+
+In `param`, you can pass an object that specifies the following options:
+
+|Property|Type|Description|
+|---------|---|------|
+|mailType|Text|Indicates the Mail type to use to send and receive emails. Possible types are:
- "MIME"
- "JMAP"|
+
+### Returned object
+
+The returned `Google` object contains the following properties:
+
+|Property||Type|Description|
+|----|-----|---|------|
+|mail||Object|Email handling object|
+||[send()](#googlemailsend)|Function|Sends the emails|
+||type|Text|(read-only) Mail type used to send and receive emails. Can be set using the `mailType` option|
+||userId|Text|User identifier, used to identify the user in Service mode. Can be the `id` or the `userPrincipalName`|
+
+### Example
+
+To create the OAuth2 connection object and a Google object:
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $google : cs.NetKit.Google
+
+$oAuth2:=New OAuth2 provider($param)
+$google:=cs.NetKit.Google.new($oAuth2;New object("mailType"; "MIME"))
+```
+
+## Calendar
+
+### Google.Calendar.getCalendar()
+
+**Google.Calendar.getCalendar**( { *id* : Text } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|id|Text|->|ID of the calender to retrieve. |
+|calendar|Object|<-| Object containing the details of the specified calendar. For more details, see the [Google Calendar API resource](https://developers.google.com/calendar/api/v3/reference/calendarList#resource).|
+
+> To retrieve calendar IDs call the getCalendars() function. If id is null, empty or missing, returns the primary calendar of the currently logged in user.
+
+#### Description
+
+`Google.Calendar.getCalendar()` retrieves a specific calendar from the authenticated user's calendar list; using an `id` to identify the calendar and returns a `calendar` object containing details about the requested calendar.
+
+#### Example
+
+```4d
+
+var $google : cs.NetKit.Google
+var $oauth2 : cs.NetKit.OAuth2Provider
+var $param; $Calendars; $myCalendar : Object
+
+$param:={}
+$param.name:="google"
+$param.permission:="signedIn"
+$param.clientId:="your-client-id" // Replace with your Google identity platform client ID
+$param.clientSecret:="xxxxxxxxx"
+$param.redirectURI:="http://127.0.0.1:50993/authorize/"
+$param.scope:=[]
+$param.scope.push("https://mail.google.com/")
+$param.scope.push("https://www.googleapis.com/auth/calendar")
+
+$oauth2:=New OAuth2 provider($param)
+
+$google:=cs.NetKit.Google.new($oauth2)
+
+// Retrieve the entire list of calendars
+$Calendars:=$google.calendar.getCalendars()
+
+// Retrieve the first calendar in the list using its ID
+$myCalendar:=$google.calendar.getCalendar($Calendars.calendars[0].id)
+
+```
+
+
+### Google.Calendar.getCalendars()
+
+**Google.Calendar.getCalendar**( { *param* : Object } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->|Set of options to filter or refine the calendar list request.|
+|Result|Object|<-|Object containing the calendar list with the related data.|
+
+#### Description
+
+`Google.Calendar.getCalendars()` retrieves a list of calendars that the authenticated user can access. The passed filtering and paging options in `param` are returned in the `result` object.
+
+In *param*, you can pass the following optional properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+| maxResults | Integer | Maximum number of calendar entries returned per page. Default is 100. Maximum is 250.|
+| minAccessRole | String | Minimum access role for the user in the returned calendars. Default is no restriction. Acceptable values:|
+| | |- "freeBusyReader": User can read free/busy information. |
+| | |- "owner": User can read, modify events, and control access. |
+| | |- "reader": User can read non-private events. |
+| | |- "writer": User can read and modify events. |
+| showDeleted | Boolean | Whether to include deleted calendar list entries in the result. Optional. The default is False.|
+| showHidden | Boolean | Whether to show hidden entries. Optional. The default is False.|
+
+#### Returned object
+
+The function returns a Collection of details about the user's calendar list in the following properties:
+
+| **Property** | **Type** | **Description** |
+|----------------------|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `calendars` | Collection | Collection of calendar objects present in the user's calendar list. Each calendar object contains details such as `id`, `summary`, and `accessRole`. |
+| `isLastPage` | Boolean | `True` if the last page of results has been reached. |
+| `page` | Integer | Current page number of results. Starts at `1`. By default, each page holds 100 results. |
+| `next()` | Function | Loads the next page of calendar entries and increments the `page` property by 1. Returns: |
+| | | - `True` if the next page is loaded successfully. |
+| | | - `False` if no additional pages are available (the collection is not updated). |
+| `previous()` | Function | Loads the previous page of calendar entries and decrements the `page` property by 1. Returns: |
+| | | - `True` if the previous page is loaded successfully. |
+| | | - `False` if no previous pages are available (the collection is not updated). |
+| `statusText` | Text | Status message returned by the Google server or the last error message from the 4D error stack. |
+| `success` | Boolean | `True` if the operation is successful, `False` otherwise. |
+| `errors` | Collection | Collection of 4D error items (if any): |
+| | | - `.errcode`: 4D error code number. |
+| | | - `.message`: Error description. |
+| | | - `.componentSignature`: Signature of the component that returned the error. |
+
+
+#### Example
+
+```4d
+
+var $google : cs.NetKit.Google
+var $oauth2 : cs.NetKit.OAuth2Provider
+var $param; $Calendars : Object
+
+$param:={}
+$param.name:="google"
+$param.permission:="signedIn"
+$param.clientId:="your-client-id" // Replace with your Google identity platform client ID
+$param.clientSecret:="xxxxxxxxx"
+$param.redirectURI:="http://127.0.0.1:50993/authorize/"
+$param.scope:=[]
+$param.scope.push("https://mail.google.com/")
+$param.scope.push("https://www.googleapis.com/auth/calendar")
+
+$oauth2:=New OAuth2 provider($param)
+
+$google:=cs.NetKit.Google.new($oauth2)
+
+// Retrieve the entire list of calendars
+
+$Calendars:=$google.calendar.getCalendars()
+
+```
+### Google.Calendar.getEvent()
+
+**Google.Calendar.getEvent**( *param* : Object ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->|Object containing the necessary details to retrieve a specific event|
+|Result|Object|<-|Object containing the retrieved event|
+
+#### Description
+
+`Google.Calendar.getEvent()` retrieves a specific event from a Google Calendar using its unique `eventId`.
+
+In *param*, you can pass the following properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+| eventId | String | (Required) The unique identifier of the event to retrieve |
+| calendarId | String | Calendar identifier. To retrieve calendar IDs, call calendarList.list(). If not provided, the user's primary (currently logged-in) calendar is used |
+| maxAttendees | Integer | Max number of attendees to be returned for the event|
+| timeZone | String | Time zone used in the response (formatted as an IANA Time Zone Database name, e.g., "Europe/Zurich"). Defaults to UTC |
+
+#### Returned object
+
+The function returns a Google [`event`](https://developers.google.com/calendar/api/v3/reference/events) object.
+
+### Google.Calendar.getEvents()
+
+**Google.Calendar.getEvents**( { *param* : Object } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->|Object containing filters and options for retrieving calendar events|
+|Result|Object|<-|Object containing the retrieved events|
+
+#### Description
+
+`Google.Calendar.getEvents()` retrieves events on the specified calendar. If *param* is not provided, it returns all events from the user's primary calendar.
+
+In *param*, you can pass the following optional properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+| calendarId | String | Calendar identifier. To retrieve calendar IDs, call `Google.Calendar.getCalendars()`. If not provided, the user's primary calendar is used. |
+| eventTypes | String | Specifies the types of events to return. Can be repeated multiple times to retrieve multiple event types. If not set, all event types are returned. Acceptable values: "birthday" (special all-day events with annual recurrence), "default" (regular events), "focusTime" (focus time events), "fromGmail" (events from Gmail), "outOfOffice" (out-of-office events), "workingLocation" (working location events). |
+| iCalUID | String | Searches for an event by its iCalendar ID. **Note:** `icalUID` and `id` are not identical. In recurring events, all occurrences have unique `id`s but share the same `icalUID`. |
+| maxAttendees | Integer | Limits the number of attendees to be returned per event|
+| top | Integer | Mximum number of events per page. Default is `250`, maximum is `2500`. |
+| orderBy | String | Specifies how events should be ordered in the response. Default is an **unspecified but stable order**. Acceptable values: "startTime" (ascending, only when `singleEvents=True`), "updated" (ascending order of last modification time). |
+| privateExtendedProperty | Collection | Returns events that match these properties specified as propertyName=value |
+| search | String | Searches for events using free text in multiple fields, including summary, description, location, attendee names/emails, organizer names/emails, and working location properties. Also matches predefined keywords for out-of-office, focus-time, and working-location events. |
+| sharedExtendedProperty | Collection | Returns events that match these properties specified as propertyName=value. The returned events match **all** specified constraints |
+| showDeleted | Boolean | Whether to include deleted events (`status="cancelled"`) in the result. Defaults to `False`. Behavior depends on the `singleEvents` setting |
+| showHiddenInvitations | Boolean | Whether to include hidden invitations in the result. Defaults to `False` |
+| singleEvents | Boolean | Whether to expand recurring events into instances and return only individual events and instances, **excluding** the underlying recurring event. Defaults to `False` |
+| startDateTime | Text, Object | Filters events by start time. If set, `endDateTime` must also be provided. **Text:** ISO 8601 UTC timestamp. **Object:** Must contain `date` (date type) and `time` (time type), formatted according to system settings |
+| endDateTime | Text, Object | Filters events by end time. If set, `startDateTime` must also be provided. **Text:** ISO 8601 UTC timestamp. **Object:** Must contain `date` (date type) and `time` (time type), formatted according to system settings |
+| timeZone | String | Time zone for the response, formatted as an IANA Time Zone Database name (e.g., "Europe/Zurich"). Defaults to UTC |
+| updatedMin | Text | Filters events based on last modification time (`ISO 8601 UTC`). When set, deleted events since this time are always included, regardless of `showDeleted` |
+
+#### Returned object
+
+The method returns a [**status object**](#status-object-google-class) in addition to the following properties:
+
+| Property | Type | Description |
+|---| ---|---|
+| isLastPage | Boolean | True if the last page is reached. |
+| page | Integer | Page number of the user information. Defaults to 1, with a page size of 100 (configurable via top). |
+| next() | Function | Fetches the next page of users, increments page by 1. Returns True if successful, False otherwise. |
+| previous() | Function | Fetches the previous page of users, decrements page by 1. Returns True if successful, False otherwise. |
+| kind | String | Type of collection ("calendar#events"). |
+| etag | String | ETag of the collection. |
+| summary | String | Title of the calendar (read-only). |
+| calendarId | String | Calendar identifier, same as the calendarId passed in the parameter if present. |
+| description | String | Description of the calendar (read-only). |
+| updated | Text | Last modification time of the calendar (ISO 8601 UTC). |
+| timeZone | String | Time zone of the calendar (formatted as an IANA Time Zone Database name, e.g., "Europe/Zurich"). |
+| accessRole | String | User’s access role for the calendar (read-only). Possible values: "none", "freeBusyReader", "reader", "writer", "owner". |
+| defaultReminders | Collection | Default reminders for the authenticated user. Applies to events that do not explicitly override them. |
+| defaultReminders[].method | String | Method used for the reminder ("email" or "popup"). |
+| defaultReminders[].minutes | Integer | Minutes before the event when the reminder triggers. |
+| events | Collection | List of events on the calendar. If some events have attachments, an "attachments" attribute is added, containing a collection of attachments. |
+
+#### Example
+
+```4d
+
+// Gets all the calendars
+var $calendars:=$google.calendar.getCalendars()
+// For the rest of the example, we'll use the first calendar in the list
+var $myCalendar:=$calendars.calendars[0]
+
+// Gets all the event of the selected calendars
+var $events:=$google.calendar.getEvents({calendarId: $myCalendar.id; top: 10})
+
+```
+
+### Google.calendar.createEvent()
+
+**Google.calendar.createEvent**(*event*: Object{; *param*: Object}) : Object
+
+#### Parameters
+
+| Parameter | Type | | Description|
+| -------- | ------ | --- |--------------------------- |
+|event | Object | ->|Object containing details of the calendar [event](#event-object) to create |
+|param | Object | -> | Object containing additional creation options |
+|Result|Object|<-|[Status object](#status-object-google-class)|
+
+#### Description
+
+`Google.calendar.createEvent()` creates a new calendar event.
+
+In *event*, pass an [event](#event-object) object to create. Only `start` and `end` properties are required.
+
+In *param*, you can pass the following additional optional properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|sendUpdates|String|Defines who should receive email notifications about the event. Acceptable values:
• `"all"` – Notify all attendees.
• `"externalOnly"` – Notify only non-Google users.
• `"none"` – No notifications sent.|
+|supportsAttachments|Boolean| `true` to allow creation or modification of the [`attachments`](#attachment-object-google) property. Defaults to `false` |
+
+#### Returned Object
+
+The method returns a [**status object**](status-object-google-class) with an additional "event" property:
+
+|Property|Type|Description|
+|---------|--- |------|
+|event|Object|[Event object](#event-object) returned by the server|
+|success|Boolean| [see Status object](#status-object-google-class)|
+|statusText|Text| [see Status object](#status-object-google-class)|
+|errors|Collection| [see Status object](#status-object-google-class)|
+
+#### Example
+
+Create an event in the Google calendar:
+
+```4d
+var $Google:=cs.NetKit.Google.new($Oauth)
+var $event; $result : Object
+
+$event:={}
+$event.summary:="Team Meeting"
+$event.start:={date: Current date; time: Current time}
+$event.end:={date: Current date; time: Current time+3600}
+$event.attendees:=[{email: "first.lastname@gmail.com"}]
+$event.description:="description of the event"
+
+$result:=$Google.calendar.createEvent($event)
+If (Not($result.success))
+ ALERT($result.statusText)
+End if
+```
+
+### Google.calendar.updateEvent()
+
+**Google.calendar.updateEvent**(*event*: Object{; *param*: Object}) : Object
+
+#### Parameters
+
+| Parameter | Type | | Description|
+| -------- | ----- | -------- | --------------- |
+|event |Object|->| Object containing details of the calendar [event](#event-object) to update. |
+|param |Object|->| Object containing additional update options. |
+|Result | Object | <-| [Status object](#status-object-google-class) |
+
+#### Description
+
+`Google.calendar.updateEvent()` updates an existing event.
+
+In *event*, pass an [event](#event-object) object to create. `start`, `end` and `id` properties are required.
+
+And in *param*, you can pass the following additional optional properties:
+
+| Property | Type | Description |
+|------------|---------|-------------|
+| sendUpdates | String | Defines who should receive email notifications about the update. Acceptable values:
• `"all"` – Notify all attendees.
• `"externalOnly"` – Notify only non-Google users.
• `"none"` – No notifications sent. |
+| supportsAttachments | Boolean | `true` to allow creation or modification of the [`attachments`](#attachment-object-google) property. Defaults to `false`. |
+| fullUpdate | Boolean | If `true`, the full event is replaced. If `false` (default), only specified fields are updated. |
+
+#### Returned Object
+
+The method returns a [**status object**](status-object-google-class) with an additional "event" property:
+
+|Property|Type|Description|
+|---------|--- |------|
+|event|Object|Updated [event object](#event-object) returned by the server|
+|success|Boolean| [see Status object](#status-object-google-class)|
+|statusText|Text| [see Status object](#status-object-google-class)|
+|errors|Collection| [see Status object](#status-object-google-class)|
+
+#### Example
+
+Update an already existing event:
+
+```4d
+#DECLARE($eventId:Text)
+var $Google:=cs.NetKit.Google.new($Oauth)
+var $result : Object
+
+$event.id:=$eventId
+$event.summary:="Updated Event Title"
+$event.description:="Updated Event description"
+$event.start:={date: Current date; time: Current time}
+$event.end:={date: Current date; time: Current time+3600}
+
+$result:=$Google.calendar.updateEvent($event)
+If (Not($result.success))
+ ALERT($result.statusText)
+End if
+```
+
+
+### Google.calendar.deleteEvent()
+
+**Google.calendar.deleteEvent**(*param*: Object) : Object
+
+#### Parameters
+
+| Parameter | Type | | Description |
+| -------- | ----- | --- | -------------------- |
+| param | Object | -> | Object containing details of the calendar [event](#event-object) to delete |
+|Result|Object|<-|[Status object](#status-object-google-class)|
+
+#### Description
+
+`Google.calendar.deleteEvent()` deletes an event from a specified calendar.
+
+In *param*, you can pass the following properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+| eventId | String | **Required.** ID of the event to delete|
+| calendarId | String |**Optional.** Calendar ID. If not provided or null, uses the user's primary calendar. To retrieve calendar IDs, call `Google.calendar.getCalendars()`|
+| sendUpdates | String |**Optional.** Controls which attendees of the event receive notifications about the deletion. Acceptable values:
• `"all"` – Notify all attendees of the event (Google or non-Google users)
• `"externalOnly"` – Notify only non-Google users
• `"none"` – No notifications sent|
+
+#### Returned Object
+
+The method returns a standard [**status object**](#status-object-google-class).
+
+#### Example
+
+Delete a calendar event:
+
+```4d
+var $Google:=cs.NetKit.Google.new($Oauth)
+
+$status:=$google.calendar.deleteEvent({eventId: $event.id})
+If ($result.success)
+ ALERT("Calendar event correctly deleted")
+Else
+ ALERT($result.statusText)
+End if
+```
+
+### Event object
+
+The `event` object used with Google Calendar methods includes the following main properties. For the full list, refer to the [official Google Calendar API documentation](https://developers.google.com/calendar/api/v3/reference/events#resource).
+
+| Property | | Type | Description|
+| ------ |---| -------- | ------------------- |
+|id || Text | ID for the event.|
+| calendarId | | Text | Calendar ID. If not provided, the user's primary calendar is used. Use `Google.calendar.getCalendars()` to retrieve IDs.|
+| attachments | | Collection | File [attachments](#attachment-object-google) (max 25). To use this, `supportsAttachments` must be set to `true` in the request.|
+| attendees| | Collection | List of attendees. |
+| | email | String | Required. Email address of the attendee. |
+| | displayName | String | Name of the attendee. |
+| | comment| String | The attendee’s response comment. |
+| | optional| Boolean | (Default: false) Whether the attendee is optional.|
+| | resource| Boolean | (Default: false) Set to `true` when the attendee is a resource (e.g., room or equipment). Can only be set when the attendee is first added. Ignored in later updates. |
+| | additionalGuests| Integer | (Default: `0`) Allowed number of additional guests of the attendee. |
+| description | | Text | Description of the event (HTML allowed).|
+| start | | Object | Start time. Use `dateTime` with optional `timeZone`, or `date` for all-day events.|
+| | date | Date, Text | Start date of the event. If provided as text, use the format `"yyyy-mm-dd"`. |
+| | time | Time |Start time of the event (not present if all-day event)|
+| | dateTime | Text | Combined start date and time in RFC3339 format. A time zone offset is required unless `timeZone` is specified. IOverrides `date` and `time`.(not used for all-day events). |
+| | timeZone | String | Time zone for the `dateTime`, using IANA format (e.g., `"Europe/Zurich"`). Defaults to UTC if not provided. |
+| end | | Object | End time. Use `dateTime` with optional `timeZone`, or `date` for all-day events.|
+| | date | Date, Text | End date of the event. If provided as text, use the format `"yyyy-mm-dd"`. |
+| | time | Time |End time of the event (not present if all-day event)|
+| | dateTime | Text |Combined date and time in RFC3339 format. A time zone offset is required unless `timeZone` is specified. Overrides `date` and `time`.(not used for all-day events). |
+| | timeZone | String | Time zone for the `dateTime`, using IANA format (e.g., `"Europe/Zurich"`). Defaults to UTC if not provided. |
+| eventType | | Text | Specific type of the event (Cannot be changed after creation).
Possible values: `"default"`, `"birthday"`, `"focusTime"`, `"outOfOffice"`, etc.|
+| extendedProperties.private | | Object | [Custom key-value pairs](https://developers.google.com/calendar/api/v3/reference/events#extendedProperties) only visible to the event owner to store additional information (e.g; `"internalNote": "Discuss Q3 targets"`)|
+| extendedProperties.shared | | Object | [Custom key-value pairs](https://developers.google.com/calendar/api/v3/reference/events#extendedProperties) shared with all attendees to share additional notes or tags (e.g., `"projectCode": "XYZ123"`).|
+| focusTimeProperties| | Object | [Focus Time event-specific settings](https://developers.google.com/calendar/api/v3/reference/events#focustimeproperties). Used when `eventType` is `"focusTime"`.|
+| guestsCanInviteOthers | | Boolean | (Default: true) If attendees can invite guests. |
+| guestsCanModify | | Boolean | (Default: false) If attendees can edit the event. |
+| guestsCanSeeOtherGuests| | Boolean | (Default: true) If attendees can see each other.|
+| location | | Text | Event location.|
+| recurrence\[] | | Collection | List of rules for repeating events using [RFC5545](https://www.rfc-editor.org/rfc/rfc5545) format (RRULE/EXRULE/RDATE/EXDATE (e.g., FREQ=WEEKLY;BYDAY=MO)). Does not include start/end times, use the `start` and `end` for that. Omit this field for one-time events. |
+| reminders.overrides\[] | | Collection | Custom reminders.|
+| | method | String | **Required**. Method of the reminder: "email" or "popup"|
+| | minutes | Integer | **Required**. Time before event (in minutes) when reminder should trigger. Between 0 and 40320.|
+| reminders.useDefault | | Boolean | Whether to use the calendar’s default reminders for the event.|
+| source.title | | Text | Title of the source linked to the event, such as a web page or email subject.|
+| source.url | | Text | URL of the source linked to the event (must use `http` or `https`).|
+| status | | Text |Describes the event's current state.
Possible values: `"confirmed"` (default) `"tentative"`, or `"cancelled"`.|
+| summary | | Text | Title of the event.|
+| transparency | | Text | Whether the event blocks time on the calendar. Values: `"opaque"` (busy) or `"transparent"` (available).|
+| visibility | | Text | Visibility level: `"default"`, `"public"`, `"private"`, or `"confidential"`.|
+
+## Mail
+
+### Google.mail.append()
+
+**Google.mail.append**( *mail* : Text { ; *labelIds* : Collection } ) : Object
+**Google.mail.append**( *mail* : Blob { ; *labelIds* : Collection } ) : Object
+**Google.mail.append**( *mail* : Object { ; *labelIds* : Collection } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mail|Text | Blob | Object|->|Email to be append |
+|labelIds|Collection|->|Collection of label IDs to add to messages. By default the DRAFT label is applied|
+|Result|Object|<-|[Status object](#status-object-google-class)|
+
+
+#### Description
+
+`Google.mail.append()` appends *mail* to the user's mailbox as a DRAFT or with designated *labelIds*.
+
+>If the *labelIds* parameter is passed and the mail has a "from" or "sender" header, the Gmail server automatically adds the SENT label.
+
+#### Returned object
+
+The method returns a [**status object**](status-object-google-class) with an additional "id" property:
+
+|Property|Type|Description|
+|---------|--- |------|
+|id|Text|id of the email created on the server|
+|success|Boolean| [see Status object](#status-object-google-class)|
+|statusText|Text| [see Status object](#status-object-google-class)|
+|errors|Collection| [see Status object](#status-object-google-class)|
+
+#### Example
+
+To append an email :
+
+```4d
+$status:=$google.mail.append($mail)
+```
+
+By default, the mail is created with a DRAFT label. To change the designated label, pass a second parameter:
+
+```4d
+$status:=$google.mail.append($mail;["INBOX"])
+```
+
+### Google.mail.createLabel()
+
+**Google.mail.createLabel**( *labelInfo* : Object ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|[labelInfo](#labelinfo-object)|Object|->|Label information.|
+|Result|Object|<-|[Status object](#status-object-google-class)|
+
+#### Description
+
+`Google.mail.createLabel()` creates a new label.
+
+#### Returned object
+
+The method returns a [**status object**](status-object-google-class) with an additional "label" property:
+
+|Property|Type|Description|
+|---------|--- |------|
+|label|Object|contains a newly created instance of Label (see [labelInfo](#labelinfo-object))|
+|success|Boolean| [see Status object](#status-object-google-class)|
+|statusText|Text| [see Status object](#status-object-google-class)|
+|errors|Collection| [see Status object](#status-object-google-class)|
+
+#### Example
+
+To create a label named 'Backup':
+
+```4d
+$status:=$google.mail.createLabel({name: "Backup"})
+$labelId:=$status.label.id
+```
+
+### Google.mail.delete()
+
+**Google.mail.delete**( *mailID* : Text { ; *permanently* : Boolean } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailID|Text|->|ID of the mail to delete |
+|permanently|Boolean|->|if permanently is true, deletes a message permanently. Otherwise, moves the specified message to the trash |
+|Result|Object|<-|[Status object](#status-object-google-class)|
+
+
+#### Description
+
+`Google.mail.delete()` deletes the specified message from the user's mailbox.
+
+#### Returned object
+
+The method returns a standard [**status object**](#status-object-google-class).
+
+#### Permissions
+
+This method requires one of the following OAuth scopes:
+
+```
+https://mail.google.com/
+https://www.googleapis.com/auth/gmail.modify
+```
+
+#### Example
+
+To delete an email permanently:
+
+```4d
+$status:=$google.mail.delete($mailId; True)
+```
+
+### Google.mail.deleteLabel()
+
+**Google.mail.deleteLabel**( *labelId* : Text ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|labelId|Text|->|The ID of the label|
+|Result|Object|<-|[Status object](#status-object-google-class)|
+
+#### Description
+
+`Google.mail.deleteLabel()` immediately and permanently deletes the specified label and removes it from any messages and threads that it is applied to.
+> This method is only available for labels with type="user".
+
+
+#### Returned object
+
+The method returns a standard [**status object**](#status-object-google-class).
+
+#### Example
+
+To delete a label:
+
+```4d
+$status:=$google.mail.deleteLabel($labelId)
+
+```
+
+### Google.mail.getLabel()
+
+**Google.mail.getLabel**( *labelId* : Text ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|labelId|Text|->|The ID of the label|
+|Result|Object|<-|[labelInfo](#labelinfo-object)|
+
+#### Description
+
+`Google.mail.getLabel()` returns the information of a label as a [labelInfo](#labelinfo-object) object.
+
+#### Returned object
+
+The returned [**labelInfo**](#labelinfo-object) object contains the following additional properties:
+
+
+|Property|Type|Description|
+|---------|---|------|
+|messagesTotal|Integer|The total number of messages with the label.|
+|messagesUnread|Integer|The number of unread messages with the label.|
+|threadsTotal|Integer|The total number of threads with the label.|
+|threadsUnread|Integer|The number of unread threads with the label.|
+
+#### Example
+
+To retrieve the label name, total message count, and unread messages:
+
+```4d
+$info:=$google.mail.getLabel($labelId)
+$name:=$info.name
+$emailNumber:=$info.messagesTotal
+$unread:=$info.messagesUnread
+```
+
+### Google.mail.getLabelList()
+
+**Google.mail.getLabelList**() : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|Result|Object|<-| Status object |
+
+#### Description
+
+`Google.mail.getLabelList()` returns an object containing the collection of all labels in the user's mailbox.
+
+
+#### Returned object
+
+The method returns a [**status object**](status-object-google-class) with an additional "labels" property:
+
+|Property|Type|Description|
+|---------|--- |------|
+|labels|Collection|Collection of [`mailLabel` objects](#maillabel-objects)|
+|success|Boolean| [see Status object](#status-object-google-class)|
+|statusText|Text| [see Status object](#status-object-google-class)|
+|errors|Collection| [see Status object](#status-object-google-class)|
+
+
+#### mailLabel object
+
+A `mailLabel` object contains the following properties (note that additional information can be returned by the server):
+
+|Property|Type|Description|
+|---------|--- |------|
+|name|Text|Display name of the label.|
+|id|Text|Immutable ID of the label.|
+|messageListVisibility|Text|Visibility of messages with this label in the message list in the Gmail web interface. Can be "show" or "hide"|
+|labelListVisibility|Text|Visibility of the label in the label list in the Gmail web interface. Can be:
- "labelShow": Show the label in the label list.
- "labelShowIfUnread": Show the label if there are any unread messages with that label
- "labelHide": Do not show the label in the label list.|
+|type|Text| Owner type for the label:
- "user": User labels are created by the user and can be modified and deleted by the user and can be applied to any message or thread.
- "system": System labels are internally created and cannot be added, modified, or deleted. System labels may be able to be applied to or removed from messages and threads under some circumstances but this is not guaranteed. For example, users can apply and remove the INBOX and UNREAD labels from messages and threads, but cannot apply or remove the DRAFTS or SENT labels from messages or threads.|
+
+
+### Google.mail.getMail()
+
+**Google.mail.getMail**( *mailID* : Text { ; *param* : Object } ) : Object
**Google.mail.getMail**( *mailID* : Text { ; *param* : Object } ) : Blob
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailID|Text|->|ID of the message to retrieve |
+|param|Object|->|Options for the message to retrieve|
+|Result|Object | Blob|<-| Downloaded mail|
+
+
+#### Description
+
+`Google.mail.getMail()` gets the specified message from the user's mailbox.
+
+In *param*, you can pass several properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|format|Text| The format to return the message in. Can be:
- "minimal": Returns only email message ID and labels; does not return the email headers, body, or payload. Returns a jmap object.
- "raw": Returns the full email message (default)
- "metadata": Returns only email message ID, labels, and email headers. Returns a jmap object.|
+|headers|Collection|Collection of strings containing the email headers to be returned. When given and format is "metadata", only include headers specified.|
+|mailType|Text|Only available if format is "raw". By default, the same as the *mailType* property of the mail (see [cs.NetKit.Google.new()](#csnetkitgooglenew)). If format="raw", the format can be:
- "MIME"
- "JMAP"|
+
+
+
+#### Returned object
+
+The method returns a mail in one of the following formats, depending on the `mailType`:
+
+|Format|Type|Comment|
+|---|---|---|
+|MIME|Blob||
+|JMAP|Object|Contains an `id` attribute|
+
+
+
+### Google.mail.getMailIds()
+
+**Google.mail.getMailIds**( { *param* : Object } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->|Options for messages to get |
+|Result|Object|<-| Status object |
+
+#### Description
+
+`Google.mail.getMailIds()` returns an object containing a collection of message ids in the user's mailbox.
+
+In *param*, you can pass several properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|top|Integer|Maximum number of messages to return (default is 100). The maximum allowed value for this field is 500.|
+|search|Text| Only return messages matching the specified query. Supports the same query format as the Gmail search box. For example, "from:someuser@example.com rfc822msgid:somemsgid@example.com is:unread". See also [https://support.google.com/mail/answer/7190](https://support.google.com/mail/answer/7190).|
+|labelIds|Collection| Only return messages with labels that match all of the specified label IDs. Messages in a thread might have labels that other messages in the same thread don't have. To learn more, see [Manage labels on messages and threads](https://developers.google.com/gmail/api/guides/labels) in Google documentation. |
+|includeSpamTrash|Boolean|Include messages from SPAM and TRASH in the results. False by default. |
+
+
+
+#### Returned object
+
+The method returns a [**status object**](status-object-google-class) with additional properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|isLastPage|Boolean|True if the last page is reached|
+|page|Integer|Mail information page number. Starts at 1. By default, each page holds 10 results. Page size limit can be set in the `top` *option*.|
+|next()|`4D.Function` object|Function that updates the mail collection with the next mail information page and increases the `page` property by 1. Returns a boolean value:
- If a next page is successfully loaded, returns True
- If no next page is returned, the mail collection is not updated and False is returned.|
+|previous()|`4D.Function` object|Function that updates the mail collection with the previous mail information page and decreases the `page` property by 1. Returns a boolean value:
- If a previous page is successfully loaded, returns True
- If no previous page is returned, the mail collection is not updated and False is returned.|
+|mailIds|Collection| Collection of objects, where each object contains:
- *id* : Text : The id of the email
- *threadId* : Text : The id of the thread to which this Email belongs
- If no mail is returned, the collection is empty.|
+|success|Boolean| [see Status object](#status-object-google-class)|
+|statusText|Text| [see Status object](#status-object-google-class)|
+|errors|Collection| [see Status object](#status-object-google-class)|
+
+
+#### Permissions
+
+This method requires one of the following OAuth scopes:
+
+```
+https://www.googleapis.com/auth/gmail.modify
+https://www.googleapis.com/auth/gmail.readonly
+https://www.googleapis.com/auth/gmail.metadata
+```
+
+### Google.mail.getMails()
+
+**Google.mail.getMails**( *mailIDs* : Collection { ; *param* : Object } ) : Collection
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailIDs|Collection|->|Collection of strings (mail IDs), or a collection of objects (each object contains an ID property)|
+|param|Object|->|Options|
+|Result|Collection|<-|Collection of mails in format depending on *mailType*: JMAP (collection of objects) or MIME (collection of blobs)If no mail is returned, the collection is empty|
+
+
+#### Description
+
+`Google.mail.getMails()` gets a collection of emails based on the specified *mailIDs* collection.
+
+> The maximum number of IDs supported is 100. In order to get more than 100 mails, it's necessary to call the function multiple times; otherwise, the `Google.mail.getMails()` function returns null and throws an error.
+
+In *param*, you can pass several properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|format|Text| The format to return the message in. Can be:
- "minimal": Returns only email message ID and labels; does not return the email headers, body, or payload. Returns a jmap object.
- "raw": Returns the full email message (default)
- "metadata": Returns only email message ID, labels, and email headers. Returns a jmap object.|
+|headers|Collection|Collection of strings containing the email headers to be returned. When given and format is "metadata", only include headers specified.|
+|mailType|Text|Only available if format is "raw". By default, the same as the *mailType* property of the mail (see [cs.NetKit.Google.new()](#csnetkitgooglenew)). If format="raw", the format can be:
- "MIME"
- "JMAP"(Default)|
+
+
+
+#### Returned value
+
+The method returns a collection of mails in one of the following formats, depending on the `mailType`:
+
+|Format|Type|Comment|
+|---|---|---|
+|MIME|Blob||
+|JMAP|Object|Contains an `id` attribute|
+
+
+
+### Google.mail.send()
+
+**Google.mail.send**( *email* : Text ) : Object
**Google.mail.send**( *email* : Object ) : Object
**Google.mail.send**( *email* : Blob ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|email|Text | Blob | Object|->| Email to be sent|
+|Result|Object|<-| [Status object](#status-object-google-class) |
+
+#### Description
+
+`Google.mail.send()` sends an email using the MIME or JMAP formats.
+
+In `email`, pass the email to be sent. Possible types:
+
+* Text or Blob: the email is sent using the MIME format
+* Object: the email is sent using the JSON format, in accordance with the [4D email object format](https://developer.4d.com/docs/API/EmailObjectClass.html#email-object), which follows the JMAP specification.
+
+The data type passed in `email` must be compatible with the [`Google.mail.type` property](#returned-object-2). In the following example, since the mail type is `JMAP`, `$email` must be an object:
+
+```4d
+$Google:=cs.NetKit.Google.new($token;{mailType:"JMAP"})
+$status:=$Google.mail.send($email)
+```
+
+> To avoid authentication errors, make sure your application has appropriate authorizations to send emails. One of the following OAuth scopes is required: [modify](https://www.googleapis.com/auth/gmail.modify), [compose](https://www.googleapis.com/auth/gmail.compose), or [send](https://www.googleapis.com/auth/gmail.send). For more information, see the [Authorization guide](https://developers.google.com/workspace/guides/configure-oauth-consent).
+
+#### Returned object
+
+The method returns a standard [**status object**](#status-object-google-class).
+
+### Google.mail.untrash()
+
+**Google.mail.untrash**( *mailID* : Text ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailID|Text|->|The ID of the message to remove from Trash |
+|Result|Object|<-|[Status object](#status-object-google-class)|
+
+
+#### Description
+
+`Google.mail.untrash()` removes the specified message from the trash.
+
+#### Returned object
+
+The method returns a standard [**status object**](#status-object-google-class).
+
+#### Permissions
+
+This method requires one of the following OAuth scopes:
+
+```
+https://mail.google.com/
+https://www.googleapis.com/auth/gmail.modify
+```
+
+### Google.mail.update()
+
+**Google.mail.update**( *mailIDs* : Collection ; *param* : Object) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailIDs|Collection|->|Collection of strings (mail IDs), or collection of objects (each object contains an ID property)|
+|param|Object|->|Options|
+|Result|Object|<-| [Status object](#status-object-google-class) |
+
+> There is a limit of 1000 IDs per request.
+
+#### Description
+
+`Google.mail.update()` adds or removes labels on the specified messages to help categorizing emails. The label can be a system label (e.g., NBOX, SPAM, TRASH, UNREAD, STARRED, IMPORTANT) or a custom label. Multiple labels could be applied simultaneously.
+
+For more information check out the [label management documentation](https://developers.google.com/gmail/api/guides/labels).
+
+In *param*, you can pass the following two properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|addLabelIds|Collection|A collection of label IDs to add to messages.|
+|removeLabelIds|Collection|A collection of label IDs to remove from messages.|
+
+
+#### Returned object
+
+The method returns a standard [**status object**](#status-object-google-class).
+
+
+#### Example
+
+To mark a collection of emails as "unread":
+
+```4d
+$result:=$google.mail.update($mailIds; {addLabelIds: ["UNREAD"]})
+```
+
+### Google.mail.updateLabel()
+
+**Google.mail.updateLabel**( *labelId* : Text ; *labelInfo* : Object ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|labelId|Text|->|The ID of the label|
+|[labelInfo](#labelinfo-object)|Object|->|Label information to update|
+|Result|Object|<-|[Status object](#status-object-google-class)|
+
+#### Description
+
+`Google.mail.updateLabel()` updates the specified label.
+> This method is only available for labels with type="user".
+
+#### Returned object
+
+The method returns a [**status object**](status-object-google-class) with an additional "label" property:
+
+|Property|Type|Description|
+|---------|--- |------|
+|label|Object|contains an instance of Label (see [labelInfo](#labelinfo-object))|
+|success|Boolean| [see Status object](#status-object-google-class)|
+|statusText|Text| [see Status object](#status-object-google-class)|
+|errors|Collection| [see Status object](#status-object-google-class)|
+
+#### Example
+
+To update a previously created label to 'Backup January':
+
+```4d
+$status:=$google.mail.updateLabel($labelId; {name:"Backup January"})
+
+```
+
+### "Google" mail object properties
+
+When you send an email with the "Google" mail type, you must pass an object to `Google.mail.send()`. For a comprehensive list of properties supported by Gmail message objects, refer to the [Gmail API documentation](https://developers.google.com/gmail/api/reference/rest/v1/users.messages). The most common properties are listed below:
+
+| Property | Type | Description |
+|----------|------|-------------|
+| attachments | attachment collection | The attachments for the email. |
+| bccRecipients | recipient collection | The Bcc: recipients for the message. |
+| ccRecipients | recipient collection | The Cc: recipients for the message. |
+| from | recipient object | The sender's email address. Must match the authenticated Gmail user. |
+| id | Text | Unique identifier for the message. |
+| important | Boolean | If true, marks the message as important (Gmail only). |
+| labelIds | Collection | List of label IDs to apply to the message. |
+| replyTo | recipient collection | Email addresses to use when replying. |
+| sender | recipient object | The account that generates the message. Same as `from` in most cases. |
+| subject | Text | The subject line of the message. |
+| toRecipients | recipient collection | The To: recipients for the message. |
+| threadId | Text | The ID of the thread to which the message belongs. |
+
+
+#### Attachment object (Google)
+
+| Property | Type | Description |
+|----------|------|-------------|
+| filename | Text | The name of the attached file. |
+| mailType | Text | Indicates the Mail type to use to send and receive email's attechement. |
+| content | Text | The base64-encoded content of the file. |
+| size | Number | The size of the file in bytes. |
+| isInline | Boolean | Set to true if the attachment is inline (e.g., embedded image). |
+| contentId | Text | Content ID for referencing the attachment inline via CID. |
+
+
+#### recipient object
+
+| Property | Type | Description |
+|----------|------|-------------|
+| emailAddress | Object | Contains the address and display name. |
+| emailAddress.address | Text | The email address of the recipient. |
+| emailAddress.name | Text | Display name of the recipient. |
+
+#### Example: Send an email with a file attachment (Google)
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $token; $param; $email; $status : Object
+
+// Set up authentication
+$param:=New object()
+$param.name:="Google"
+$param.permission:="signedIn"
+$param.clientId:="your-client-id" // Replace with your client ID
+$param.redirectURI:="http://127.0.0.1:50993/authorize/"
+$param.scope:="https://www.googleapis.com/auth/gmail.send"
+
+$oAuth2:=New OAuth2 provider($param)
+$token:=$oAuth2.getToken()
+
+// Create the email, specify the sender and the recipient
+$email:=New object()
+$email.from:=New object("emailAddress"; New object("address"; "sender@gmail.com"))
+$email.toRecipients:=New collection(New object("emailAddress"; New object("address"; "recipient@gmail.com")))
+$email.subject:="Hello from NetKit"
+$email.body:=New object("content"; "Hello, World!"; "contentType"; "html")
+
+// Create an attachment
+var $attachment : Object
+var $text : Text
+$text:="Simple text file"
+BASE64 ENCODE($text)
+$attachment:=New object
+$attachment.filename:="note.txt"
+$attachment.mimeType:="text/plain"
+$attachment.content:=$text
+$email.attachments:=New collection($attachment)
+
+// Send the email
+var $Google : Object
+$Google:=New Google($token)
+$status:=$Google.mail.send($email)
+```
+
+
+
+### labelInfo object
+
+Several Google.mail label management methods use a `labelInfo` object, containing the following properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|id|Text|The ID of the label.|
+|name|Text|The display name of the label. (mandatory)|
+|messageListVisibility|Text|The visibility of messages with this label in the message list.
Can be:
- "show": Show the label in the message list. <
- "hide": Do not show the label in the message list. |
+|labelListVisibility|Text|The visibility of the label in the label list.
Can be:
- "labelShow": Show the label in the label list.
- "labelShowIfUnread" : Show the label if there are any unread messages with that label.
- "labelHide": Do not show the label in the label list. |
+|[color](https://developers.google.com/gmail/api/reference/rest/v1/users.labels?hl=en#color)|Object|The color to assign to the label (color is only available for labels that have their type set to user).
The color object has 2 attributes :
- textColor: text: The text color of the label, represented as hex string. This field is required in order to set the color of a label.
- backgroundColor: text: The background color represented as hex string #RRGGBB (ex for black: #000000). This field is required in order to set the color of a label. |
+|type|Text|The owner type for the label.
Can be:
- "system": Labels created by Gmail.
- "user": Custom labels created by the user or application.
System labels are internally created and cannot be added, modified, or deleted. They're may be able to be applied to or removed from messages and threads under some circumstances but this is not guaranteed. For example, users can apply and remove the INBOX and UNREAD labels from messages and threads, but cannot apply or remove the DRAFTS or SENT labels from messages or threads. User labels are created by the user and can be modified and deleted by the user and can be applied to any message or thread. |
+
+## User
+
+### Google.user.get()
+
+**Google.user.get**( *id* : Text {; *select* : Text } ) : Object
+**Google.user.get**( *id* : Text {; *select* : Collection } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|id|Text|->|The *resourceName* of the person to provide information about. Use the *resourceName* field returned by [Google.user.list()](#googleuserlist) to specify the person.|
+|select|Text \| Collection|->|Text: A comma-separated list of specific fields that you want to retrieve from each person (e.g., "names, phoneNumbers").
Collection: Collection of the specific fields.|
+|Result|Object|<-|Represents user's details, like names, emails, and phone numbers based on the selected fields.|
+
+#### Description
+
+`Google.user.get()` provides information about a [user](https://developers.google.com/people/api/rest/v1/people#Person) based on the *resourceName* provided in `id` and fields optionally specified in `select`.
+
+Supported fields include *addresses*, *ageRanges*, *biographies*, *birthdays*, *calendarUrls*, *clientData*, *coverPhotos*, *emailAddresses*, *events*, *externalIds*, *genders*, *imClients*, *interests*, *locales*, *locations*, *memberships*, *metadata*, *miscKeywords*, *names*, *nicknames*, *occupations*, *organizations*, *phoneNumbers*, *photos*, *relations*, *sipAddresses*, *skills*, *urls*, *userDefined*.
+
+
+#### Returned object
+
+The returned [user object](https://developers.google.com/people/api/rest/v1/people#Person) contains values for the specified field(s).
+
+If no fields have been specified in `select`, `Google.user.get()` returns *emailAddresses* and *names*. Otherwise, it returns only the specified field(s).
+
+#### Permissions
+
+No authorization required to access public data. For private data, one of the following OAuth scopes is required:
+
+https://www.googleapis.com/auth/contacts
+https://www.googleapis.com/auth/contacts.readonly
+https://www.googleapis.com/auth/contacts.other.readonly
+https://www.googleapis.com/auth/directory.readonly
+https://www.googleapis.com/auth/profile.agerange.read
+https://www.googleapis.com/auth/profile.emails.read
+https://www.googleapis.com/auth/profile.language.read
+https://www.googleapis.com/auth/user.addresses.read
+https://www.googleapis.com/auth/user.birthday.read
+https://www.googleapis.com/auth/user.emails.read
+https://www.googleapis.com/auth/user.gender.read
+https://www.googleapis.com/auth/user.organization.read
+https://www.googleapis.com/auth/user.phonenumbers.read
+https://www.googleapis.com/auth/userinfo.email
+https://www.googleapis.com/auth/userinfo.profile
+https://www.googleapis.com/auth/profile.language.read
+
+### Google.user.getCurrent()
+
+**Google.user.getCurrent**( { *select* : Text } ) : Object
+**Google.user.getCurrent**( { *select* : Collection } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|select|Text \| Collection|->|Text: A comma-separated list of specific fields that you want to retrieve from each person (e.g., "names, phoneNumbers").
Collection: Collection of the specific fields.|
+|Result|Object|<-|Represents user's details, like names, emails, and phone numbers based on the selected fields.|
+
+#### Description
+
+`Google.user.getCurrent()` provides information about the authenticated [user](https://developers.google.com/people/api/rest/v1/people#Person) based on fields specified in `select`.
+
+Supported fields include *addresses*, *ageRanges*, *biographies*, *birthdays*, *calendarUrls*, *clientData*, *coverPhotos*, *emailAddresses*, *events*, *externalIds*, *genders*, *imClients*, *interests*, *locales*, *locations*, *memberships*, *metadata*, *miscKeywords*, *names*, *nicknames*, *occupations*, *organizations*, *phoneNumbers*, *photos*, *relations*, *sipAddresses*, *skills*, *urls*, *userDefined*.
+
+#### Returned object
+
+The returned [user object](https://developers.google.com/people/api/rest/v1/people#Person) contains values for the specific field(s).
+
+If no fields have been specified in `select`, `Google.user.getCurrent()` returns *emailAddresses* and *names*. Otherwise, it returns only the specified field(s).
+
+#### Permissions
+
+Requires the same OAuth scope package as [Google.user.get()](#permissions-15).
+
+#### Example
+
+To retrieve information from the current user:
+
+```4d
+var $google : cs.NetKit.Google
+var $oauth2 : cs.NetKit.OAuth2Provider
+var $param : Object
+
+// Set up parameters:
+$param:={}
+$param.name:="google"
+$param.permission:="signedIn"
+$param.clientId:="your-client-id" // Replace with your Google identity platform client ID
+$param.clientSecret:="xxxxxxxxx"
+$param.redirectURI:="http://127.0.0.1:50993/authorize/"
+$param.scope:=[]
+$param.scope.push("https://mail.google.com/")
+
+$param.scope.push("https://www.googleapis.com/auth/contacts")
+$param.scope.push("https://www.googleapis.com/auth/contacts.other.readonly")
+$param.scope.push("https://www.googleapis.com/auth/contacts.readonly")
+$param.scope.push("https://www.googleapis.com/auth/directory.readonly")
+$param.scope.push("https://www.googleapis.com/auth/user.addresses.read")
+$param.scope.push("https://www.googleapis.com/auth/user.birthday.read")
+$param.scope.push("https://www.googleapis.com/auth/user.emails.read")
+$param.scope.push("https://www.googleapis.com/auth/user.gender.read")
+$param.scope.push("https://www.googleapis.com/auth/user.organization.read")
+$param.scope.push("https://www.googleapis.com/auth/user.phonenumbers.read")
+$param.scope.push("https://www.googleapis.com/auth/userinfo.email")
+$param.scope.push("https://www.googleapis.com/auth/userinfo.profile")
+
+
+$oauth2:=New OAuth2 provider($param)
+
+$google:=cs.NetKit.Google.new($oauth2)
+
+var $currentUser1:=$google.user.getCurrent()
+//without parameters, returns by default "emailAddresses" and "names"
+
+var $currentUser2:=$google.user.getCurrent("phoneNumbers")
+//returns the field "phoneNumbers"
+```
+
+### Google.user.list()
+
+**Google.user.list**( { *param* : Object } ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->|A set of options defining how to retrieve and filter user data|
+|Result|Object|<-|An object containing a structured collection of [user](https://developers.google.com/people/api/rest/v1/people#Person) data organized into pages|
+
+#### Description
+
+`Google.user.list()` provides a list of domain profiles or domain contacts in the authenticated user's domain directory.
+
+> If the contact sharing or the External Directory sharing is not allowed in the Google admin, the returned `users` collection is empty.
+
+In *param*, you can pass the following properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|select|Text \| Collection|Text: A comma-separated list of specific fields that you want to retrieve from each person (e.g., "names, phoneNumbers").
Collection: Collection of the specific fields.
If omitted, defaults to returning emailAddresses and names.|
+|sources|Text \| Collection|Specifies the directory source to return. Values:
- DIRECTORY_SOURCE_TYPE_UNSPECIFIED (Unspecified),
- DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT (Google Workspace domain shared contact),
- DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE (default, Workspace domain profile).|
+|mergeSources|Text \| Collection|Adds related data if linked by verified join keys such as email addresses or phone numbers.
- DIRECTORY_MERGE_SOURCE_TYPE_UNSPECIFIED (Unspecified),
- DIRECTORY_MERGE_SOURCE_TYPE_CONTACT (User owned contact).|
+|top|Integer|Sets the maximum number of people to retrieve per page, between 1 and 1000 (default is 100).|
+
+#### Returned object
+
+The returned object holds a collection of [users objects](https://developers.google.com/people/api/rest/v1/people#Person) as well as [**status object**](status-object-google-class) properties and functions that allow you to navigate between different pages of results.
+
+|Property|Type|Description|
+|---------|--- |------|
+|users|Collection|A collection of [user objects](https://developers.google.com/people/api/rest/v1/people#Person), each containing detailed information about individual users|
+|isLastPage|Boolean|Indicates whether the current page is the last one in the collection of user data.|
+|page|Integer|Represents the current page number of user information, starting from 1. By default, each page contains 100 results, but the page size limit can be adjusted using the *top* option.|
+|next()|Function|A function that retrieves the next page of user information. Returns True if successful; otherwise, returns False if there is no next page and the users collection is not updated.|
+|previous()|Function|A function that retrieves the previous page of user information. Returns True if successful; otherwise, returns False if there is no previous page and the users collection is not updated.|
+|success|Boolean| [see Status object](#status-object-google-class)|
+|statusText|Text| [see Status object](#status-object-google-class)|
+|errors|Collection| [see Status object](#status-object-google-class)|
+
+#### Permissions
+
+Requires the same OAuth scope package as [Google.user.get()](#permissions-15).
+
+#### Example
+
+To retrieve user data in a structured collection organized into pages with a maximum of `top` users per page:
+
+```4d
+var $google : cs.NetKit.Google
+var $oauth2 : cs.NetKit.OAuth2Provider
+var $param : Object
+
+$param:={}
+$param.name:="google"
+$param.permission:="signedIn"
+$param.clientId:="your-client-id" // Replace with your Google identity platform client ID
+$param.clientSecret:="xxxxxxxxx"
+$param.redirectURI:="http://127.0.0.1:50993/authorize/"
+$param.scope:=[]
+$param.scope.push("https://mail.google.com/")
+
+$param.scope.push("https://www.googleapis.com/auth/contacts")
+$param.scope.push("https://www.googleapis.com/auth/contacts.other.readonly")
+$param.scope.push("https://www.googleapis.com/auth/contacts.readonly")
+$param.scope.push("https://www.googleapis.com/auth/directory.readonly")
+$param.scope.push("https://www.googleapis.com/auth/user.addresses.read")
+$param.scope.push("https://www.googleapis.com/auth/user.birthday.read")
+$param.scope.push("https://www.googleapis.com/auth/user.emails.read")
+$param.scope.push("https://www.googleapis.com/auth/user.gender.read")
+$param.scope.push("https://www.googleapis.com/auth/user.organization.read")
+$param.scope.push("https://www.googleapis.com/auth/user.phonenumbers.read")
+$param.scope.push("https://www.googleapis.com/auth/userinfo.email")
+$param.scope.push("https://www.googleapis.com/auth/userinfo.profile")
+
+
+$oauth2:=New OAuth2 provider($param)
+
+$google:=cs.NetKit.Google.new($oauth2)
+
+var $userList:=$google.user.list({top:10})
+```
+
+
+
+
+## Status object
+
+Several Google.mail functions return a `status object`, containing the following properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|success|Boolean| True if the operation was successful|
+|statusText|Text| Status message returned by the Gmail server or last error returned by the 4D error stack|
+|errors | Collection | Collection of 4D error items (not returned if a Gmail server response is received):
- [].errcode is the 4D error code number
- [].message is a description of the 4D error
- [].componentSignature is the signature of the internal component that returned the error|
+
+Basically, you can test the `success` and `statusText` properties of this object to know if the function was correctly executed.
+
+Some functions adds specific properties to the **status object**, properties are described with the functions.
+
+
+
+## See also
+
+[Office365 Class](./Office365.md)
+[OAuth2Provider Class](./OAuth2Provider.md)
diff --git a/docs/OAuth2Provider.md b/docs/OAuth2Provider.md
new file mode 100644
index 0000000..2b0fc8d
--- /dev/null
+++ b/docs/OAuth2Provider.md
@@ -0,0 +1,280 @@
+# OAuth2Provider Class
+
+## Overview
+
+The `OAuth2Provider` class allows you to request authentication tokens to third-party web services providers in order to use their APIs in your application. This is done in two steps:
+
+1. Using the `New OAuth2 provider` component method, you instantiate an object of the `OAuth2Provider` class that holds authentication information.
+2. You call the `OAuth2ProviderObject.getToken()` class function to retrieve a token from the web service provider.
+
+Here's a diagram of the authorization process:
+
+
+This class can be instantiated in two ways:
+* by calling the `New OAuth2 provider` method
+* by calling the `cs.NetKit.OAuth2Provider.new()` function
+
+
+**Warning:** OAuth2 authentication in `signedIn` mode requires a browser. Since some servers have restrictions regarding the supported browsers (for example, check this [Google support](https://support.google.com/accounts/answer/7675428?hl=en) page), the functionality may not work properly.
+
+**Warning:** Shared objects are not supported by the 4D NetKit API.
+
+
+## Table of contents
+
+- [New OAuth2 provider](#new-oauth2-provider)
+- [OAuth2ProviderObject.getToken()](#oauth2providerobjectgettoken)
+
+
+
+
+## **New OAuth2 provider**
+
+**New OAuth2 provider**( *paramObj* : Object ) : cs.NetKit.OAuth2Provider
+
+### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|paramObj|Object|->| Determines the properties of the object to be returned |
+|Result|cs.NetKit.OAuth2Provider|<-| Object of the OAuth2Provider class|
+
+### Description
+
+`New OAuth2 provider` instantiates an object of the `OAuth2Provider` class.
+
+In `paramObj`, pass an object that contains authentication information.
+
+
+The available properties of `paramObj` are:
+
+|Parameter|Type|Description|Optional|
+|---------|--- |------|------|
+| name | text | Name of the provider. Available values: "Microsoft", "Google" or "" (if "" or undefined/null attribute, the authenticateURI and the tokenURI need to be filled by the 4D developer).|Yes
+| permission | text |- "signedIn": Azure AD/Google will sign in the user and ensure they gave their consent for the permissions your app requests (opens a web browser).
- service": the app calls [Microsoft Graph with its own identity](https://docs.microsoft.com/en-us/graph/auth-v2-service)/Google (access without a user).|No
+| clientId | text | The client ID assigned to the app by the registration portal.|No
+| redirectURI | text | (Not used in service mode) The redirect_uri of your app, i.e. the location where the authorization server sends the user once the app has been successfully authorized. Depending on the port specified in this property, the authentication response goes to the [web server of the host or of the 4D NetKit](#web-server-for-redirect-uri) when you call the [`.getToken()`](#oauth2providerobjectgettoken) class function. |No in signedIn mode, Yes in service mode
+| scope | text or collection | Text: A space-separated list of the Microsoft Graph permissions that you want the user to consent to. Collection: Collection of Microsoft Graph permissions. |Yes
+| tenant | text | Microsoft: The {tenant} value in the path of the request can be used to control who can sign into the application. The allowed values are: - "common" for both Microsoft accounts and work or school accounts (default value)
- "organizations" for work or school accounts only
- "consumers" for Microsoft accounts only
- tenant identifiers such as tenant ID or domain name.
Google (service mode only): Email address to be considered as the email address of the user for which the application is requesting delegated access. |Yes
+| authenticateURI | text | Uri used to do the Authorization request.
Default for Microsoft: "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize".
Default for Google: "https://accounts.google.com/o/oauth2/auth". |Yes
+| tokenURI | text | Uri used to request an access token.
Default for Microsoft: "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token".
Default for Google: "https://accounts.google.com/o/oauth2/token".|Yes
+|tokenExpiration | text | Timestamp (ISO 8601 UTC) that indicates the expiration time of the token.| Yes
+| clientSecret | text | The application secret that you created for your app in the app registration portal. Required for web apps. |Yes
+| token | object | If this property exists, the `getToken()` function uses this token object to calculate which request must be sent. It is automatically updated with the token received by the `getToken()` function. |Yes
+| timeout|real| Waiting time in seconds (by default 120s).|Yes
+| prompt | text |(Optional) A space-delimited, case-sensitive list of prompts to present the user.
Possible values are:
- none: Do not display any authentication or consent screens. Must not be specified with other values.
- consent: Prompt the user for consent.
- select_account: Prompt the user to select an account.
(if you don't specify this parameter, the user will be prompted only the first time your project requests access. )|Yes|
+| loginHint | text | (Optional) This option can be used to inform the Google Authentication Server which user is attempting to authenticate if your application is aware of this information. By prefilling the email field in the sign-in form or by selecting the appropriate multi-login session, the server uses the hint to simplify the login flow either.
Set the parameter value to a sub-identifier or email address that corresponds to the user's Google ID. |Yes|
+| accessType | text | (Recommended) Indicates whether your application can refresh access tokens when the user is not present at the browser.
Valid parameter values are online (default) and offline.
Set the value to offline if your application needs to update access tokens when the user is not present at the browser. This is how access tokens are refreshed. This value instructs the Google authorization server to return a refresh token and an access token the first time that your application exchanges an authorization code for tokens. |Yes|
+| clientEmail | text | (mandatory, Google / service mode only) email address of the service account used |No|
+| authenticationPage|text or file object|Path of the web page to display in the web browser when the authentication code is received correctly in signed in mode (If not present the default page is used).|Yes
+| authenticationErrorPage |text or file object| Path of the web page to display in the web browser when the authentication server returns an error in signed in mode (If not present the default page is used).|Yes
+| PKCEEnabled |boolean| false by default. If true, PKCE is used for OAuth 2.0 authentication and token requests and is only usable for permission=”SignIn”. |Yes
+| PKCEMethod |text | "S256" by default. The only supported values for this parameter are "S256" or "plain". |Yes
+| thumbprint |text | Certificate thumbprint. Only usable with permission="Service" | Yes (No for certificate based authentication)
+| privateKey | text | Certificate private key. Only usable with permission="Service".
(Google / service mode only) Private key given by Google. Mandatory if .permission="service" and .name="Google" | Yes (No for certificate based authentication)
+| clientAssertionType | text | The format of the assertion as defined by the authorization server. The value is an absolute URI. Default value: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer". Only usable with permission="Service" |Yes
+| browserAutoOpen | boolean | True (default value), the web browser is open automatically. Pass false if you don't want the web browser to open automatically. |Yes
+
+If you want the .getToken() function to use the Assertion Framework described in the RFC 7521 to connect to the server, make sure to pass the `thumbprint` and `privateKey` properties. If `clientSecret`, `thumbprint` and `privateKey` are present, the `thumbprint` is used by default and the RFC 7521 is used to connect. For more information, please refer to the [OAuth2.0 authentication using a certificate](#https://blog.4d.com/) blog post.
+
+**Note:** The `authenticationPage` and `authenticationErrorPage` and all the resources associated must be in the same folder.
+
+### Web server for redirect URI
+
+The provider's authorization response can be intercepted and handled either by the **web server of the host** or a **web server included in 4D NetKit**, depending on the port number specified in the `redirectURI` property.
+
+- If the `redirectURI` port is the same as the web server port of the host, 4D NetKit automatically uses the web server of the host to retrieve the authentication response.
+- If `redirectURI` does not specify a port, the default port is used. If the host web server is also configured with the default port, it is used; otherwise, the 4D NetKit web server is started and used.
+- In any other cases, the 4D NetKit web server is started and used.
+
+
+#### HTTP Handler
+
+If the web server of the host is used, you must install a **preconfigured HTTP handler**. You just need to add a the following lines in the `Project/Sources/HTTPHandlers.json` file of the host project:
+
+```
+[
+ {
+ "class": "4D.NetKit.OAuth2Authorization",
+ "method": "getResponse",
+ "regexPattern": "/authorize",
+ "verbs": "get"
+ },
+ ...
+```
+
+**Note:** You can define any pattern for your redirect URI, `/authorize` is a just an example. For more information, please refer to [HTTP Handlers](https://developer.4d.com/docs/WebServer/http-request-handler).
+
+#### Examples
+
+1. If the host web server is configured with the default HTTP port (80)
+
+```
+$param.redirectURI:="http://127.0.0.1:80/authorize/" //uses 4D host server
+$param.redirectURI:="http://127.0.0.1/authorize/" //uses 4D host server
+$param.redirectURI:="http://127.0.0.1:50993/authorize/" //uses 4D Netkit server
+```
+
+2. If the host web server is configured with non-default HTTP port (8080)
+
+```
+$param.redirectURI:="http://127.0.0.1:8080/authorize/" //uses 4D host server
+$param.redirectURI:="http://127.0.0.1/authorize/" //uses 4D Netkit server
+$param.redirectURI:="http://127.0.0.1:50993/authorize/" //uses 4D Netkit server
+```
+
+
+### Returned object
+
+The OAuth2 provider returned object `cs.NetKit.OAuth2Provider` properties correspond to those of the [`paramObj` object passed as a parameter](#description) and some additional properties:
+
+|Property|Type|Description|
+|----|-----|------|
+|*paramObj.properties*||< properties passed in parameter [`paramObj`](#description)>|
+|authenticateURI|text|Returns the calculated authenticateURI. Can be used in a webbrowser or in a web area to open the connection page.|
+|isTokenValid |Function| `OAuth2Provider.isTokenValid() : boolean`
Verifies the token validity.
- If no token is present, returns false.
- If the current token is not expired, returns true.
- If the token is expired and no refresh token is present, returns false.
- If a refresh token is present, automatically requests a new token and returns true if the token is generated correctly, otherwise false.|
+
+### Example 1
+
+```4d
+
+//authentication into google account and token retrieval
+
+var $File1; $File2 : 4D.File
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $param: Object
+
+$File1:=File("/RESOURCES/OK.html")
+$File2:=File("/RESOURCES/KO.html")
+
+$param:= New object
+$param.name:="Google"
+$param.permission:="signedIn"
+$param.clientId:="xxxx"
+$param.clientSecret:="xxxx"
+$param.redirectURI:="http://127.0.0.1:50993/authorize/"
+$param.scope:="https://www.googleapis.com/auth/gmail.send"
+$param.authenticationPage:=$File1
+$param.authenticationErrorPage:=$File2
+// Create new OAuth2 object
+$oAuth2:=cs.NetKit.OAuth2Provider.new($param)
+// Ask for a token
+$token:=$oAuth2.getToken()
+
+```
+### Example 2
+
+```4d
+
+//Google account authentication using PKCE
+
+var $credential:={}
+// google
+$credential.name:="Google"
+$credential.permission:="signedIn"
+$credential.clientId:="499730xxx"
+$credential.clientSecret:="fc1kwxxx"
+$credential.redirectURI:="http://127.0.0.1:50993/authorize/"
+$credential.scope:="https://mail.google.com/"
+// PKCE activation
+$credential.PKCEEnabled:=True
+
+var $oauth2:=cs.NetKit.OAuth2Provider.new($credential)
+var $token:=Try($oauth2.getToken())
+if ($token=null)
+ ALERT("Error: "+Last errors[0].message)
+end if
+
+```
+
+### Example 3
+
+```4d
+
+// Initial authentication with Microsoft OAuth2 and retrieval of token with refresh token
+
+// Define OAuth2 provider details for Microsoft
+$provider:=New object()
+$provider.name:="Microsoft"
+$provider.permission:="signedIn"
+$provider.clientId:="xxx-xxx-xxx-xxx-c460fc"
+$provider.redirectURI:="http://127.0.0.1:50993/authorize/"
+$provider.scope:="https://graph.microsoft.com/.default"
+
+// Use the "offline" parameter to request a refresh token in addition to the regular access token
+$provider.accessType:="offline"
+
+// Create new OAuth2 object for Microsoft
+$OAuth:= cs.NetKit.OAuth2Provider.new ($provider)
+
+// Request the token, which includes the refresh token
+var $myCurrentToken : Object := $OAuth.getToken()
+
+// After receiving the token and refresh token, save it for future token requests
+```
+
+
+```4d
+#DECLARE($myCurrentToken : object)
+var $provider:=New object()
+$provider.name:="Microsoft"
+$provider.permission:="signedIn"
+$provider.clientId:="xxx-xxx-xxx-xxx-c460fc"
+$provider.redirectURI:="http://127.0.0.1:50993/authorize/"
+$provider.scope:="https://graph.microsoft.com/.default"
+
+// Include the token from the previous request
+$provider.token:=$myCurrentToken
+
+// Re-create OAuth2 object with the stored token
+$OAuth:= cs.NetKit.OAuth2Provider.new ($provider)
+
+// getToken() checks if the token has expired
+// If the token is still valid, it returns the current token
+// If the token has expired, it automatically requests a new one
+// If a refresh token is present, the token is automatically renewed without user sign-in
+// If no refresh token is available, the user will need to sign in again
+$myCurrentToken:=$OAuth.getToken()
+
+```
+
+**Note**: Some servers, like Google, do not always return the refresh token during subsequent token requests. In such cases, you should remember to include the refresh token in the token object before saving it for future use.
+
+## OAuth2ProviderObject.getToken()
+
+**OAuth2ProviderObject.getToken()** : Object
+
+|Parameter|Type||Description|
+|---------|--- |------|------|
+|Result|Object|<-| Object that holds information on the token retrieved
+
+
+### Description
+
+`.getToken()` returns an object that contains a `token` property (as defined by the [IETF](https://datatracker.ietf.org/doc/html/rfc6749#section-5.1)), as well as optional additional information returned by the server:
+
+Property|Object properties|Type|Description |
+|--- |---------| --- |------|
+|token||Object| Token returned |
+|| expires_in | Text | How long the access token is valid (in seconds). |
+|| access_token |Ttext | The requested access token. |
+|| refresh_token | Text | Your app can use this token to acquire additional access tokens after the current access token expires. Refresh tokens are long-lived, and can be used to retain access to resources for extended periods of time. Available only if the value of the `permission` property is "signedIn". |
+|| token_type | Text | Indicates the token type value. The only token type that Azure AD supports is "Bearer". |
+||scope|Text| A space separated list of the Microsoft Graph permissions that the access_token is valid for.|
+|tokenExpiration || Text | Timestamp (ISO 8601 UTC) that indicates the expiration time of the token|
+
+If the value of `token` is empty, the command sends a request for a new token.
+
+If the token has expired:
+* If the token object has the `refresh_token` property, the command sends a new request to refresh the token and returns it.
+* If the token object does not have the `refresh_token` property, the command automatically sends a request for a new token.
+
+When requesting access on behalf of a user ("signedIn" mode) the command opens a web browser to request authorization.
+
+In "signedIn" mode, when `.getToken()` is called, a web server included in 4D NetKit starts automatically on the port specified in the [redirectURI parameter](#description) to intercept the provider's authorization response and display it in the browser.
+
+
+## See also
+
+[Google Class](./Google.md)
+[Office365 Class](./Office365.md)
diff --git a/docs/Office365.md b/docs/Office365.md
new file mode 100644
index 0000000..c087d0a
--- /dev/null
+++ b/docs/Office365.md
@@ -0,0 +1,1593 @@
+# Office365 class
+
+## Overview
+
+The `Office365` class allows you to call the [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/overview#data-and-services-powering-the-microsoft-365-platform) to:
+* get information from Office365 applications, such as user information
+* create, move or send emails
+
+This can be done after a valid token request, (see [OAuth2Provider object](#oauth2provider)).
+
+The `Office365` class can be instantiated in two ways:
+* by calling the `New Office365 provider` method
+* by calling the `cs.NetKit.Office365.new()` function
+
+**Warning:** Shared objects are not supported by the 4D NetKit API.
+
+
+## Table of Contents
+
+### [Initialization](##new-office365-provider)
+
+* [New Office365 provider](#new-office365-provider)
+
+### [Calendar](#calendar-1)
+
+* [Office365.calendar.getCalendar()](#office365calendargetcalendar)
+* [Office365.calendar.getCalendars()](#office365calendargetcalendars)
+* [Office365.calendar.getEvent()](#office365calendargetevent)
+* [Office365.calendar.getEvents()](#office365calendargetevents)
+* [Office365.calendar.createEvent()](#office365calendarcreateevent)
+* [Office365.calendar.updateEvent()](#office365calendarupdateevent)
+* [Office365.calendar.deleteEvent()](#office365calendardeleteevent)
+* [Office365.category.list()](#office365categorylist)
+* [Event object](#event-object)
+
+### [Mail](#mail-1)
+
+* [Office365.mail.send()](#office365mailsend)
+* [Office365.mail.append()](#office365mailappend)
+* [Office365.mail.update()](#office365mailupdate)
+* [Office365.mail.reply()](#office365mailreply)
+* [Office365.mail.createFolder()](#office365mailcreatefolder)
+* [Office365.mail.renameFolder()](#office365mailrenamefolder)
+* [Office365.mail.deleteFolder()](#office365maildeletefolder)
+* [Office365.mail.getFolder()](#office365mailgetfolder)
+* [Office365.mail.getFolderList()](#office365mailgetfolderlist)
+* [Office365.mail.getMail()](#office365mailgetmail)
+* [Office365.mail.getMails()](#office365mailgetmails)
+* [Office365.mail.move()](#office365mailmove)
+* [Office365.mail.copy()](#office365mailcopy)
+* [Office365.mail.delete()](#office365maildelete)
+* [Well-known folder names](#well-known-folder-names)
+* ["Microsoft" mail object properties](#microsoft-mail-object-properties)
+
+### [User](#user-1)
+
+* [Office365.user.get()](#office365userget)
+* [Office365.user.getCurrent()](#office365usergetcurrent)
+* [Office365.user.list()](#office365userlist)
+
+### [Status](#status-object)
+
+* [Status object (Office365 Class)](#status-object)
+
+
+## **New Office365 provider**
+
+**New Office365 provider**( *paramObj* : Object { ; *param* : Object } ) : cs.NetKit.Office365
+
+### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|paramObj|cs.NetKit.OAuth2Provider|->| Object of the OAuth2Provider class |
+|param|Object|->| Additional options |
+|Result|cs.NetKit.Office365|<-| Object of the Office365 class|
+
+### Description
+
+`New Office365 provider` instantiates an object of the `Office365` class.
+
+In `paramObj`, pass an [OAuth2Provider object](#new-auth2-provider).
+
+In `param`, you can pass an object that specifies the following options:
+
+|Property|Type|Description|
+|---------|---|------|
+|mailType|Text|Indicates the Mail type to use to send and receive emails. Possible types are:
- "MIME"
- "JMAP"
- "Microsoft" (default)|
+
+### Returned object
+
+The returned `Office365` object contains the following properties:
+
+|Property||Type|Description|
+|----|-----|---|------|
+|mail||Object|Email handling object|
+||send()|Function|Sends the emails|
+||type|Text|(read-only) Mail type used to send and receive emails. Default is "Microsoft", can bet set using the `mailType` option|
+||userId|Text|User identifier, used to identify the user in Service mode. Can be the `id` or the `userPrincipalName`|
+
+
+### Example 1
+
+To create the OAuth2 connection object and an Office365 object:
+
+```4d
+var $oAuth2: cs.NetKit.OAuth2Provider
+var $office365 : cs.NetKit.Office365
+
+$oAuth2:=New OAuth2 provider($param)
+$office365:=New Office365 provider($oAuth2;New object("mailType"; "Microsoft"))
+```
+
+### Example 2
+
+Refer to [this tutorial](#authenticate-to-the-microsoft-graph-api-in-service-mode) for an example of connection in Service mode.
+
+## Calendar
+
+### Office365.calendar.getCalendar()
+
+**Office365.calendar.getCalendar**({*id*: Text}) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|id|Text|->|ID of the calender to retrieve.|
+|calendar|Object|<-|[Object](https://learn.microsoft.com/en-us/graph/api/resources/calendar?view=graph-rest-1.0#properties) containing the properties and relationships of the specified calendar. |
+
+> To retrieve calendar IDs call the getCalendars() function. If id is null, empty or missing, returns the primary calendar of the currently logged in user.
+
+#### Description
+
+`Office365.calendar.getCalendar()` retrieves the properties and relationships of a specific calendar associated with the passed `id` and returns a `calendar` object containing the requested calendar's details.
+
+#### Example
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provid
+var $Office365 : cs.NetKit.Office365
+var $params; $calendarList; $calendarA : Object
+
+// Set up parameters:
+$params:=New object
+$params.name:="Microsoft"
+$params.permission:="signedIn"
+$params.clientId:="your-client-id" // Replace with your Microsoft identity platform client ID
+$params.redirectURI:="http://127.0.0.1:50993/authorize/"
+$params.scope:="https://graph.microsoft.com/.default"
+
+// Create an OAuth2Provider Object
+$oAuth2:=New OAuth2 provider($params)
+
+// Create an Office365 object
+$Office365:=New Office365 provider($oAuth2)
+
+
+// Retrieve the entire list of calendars
+$calendarList:=$Office365.calendar.getCalendars()
+
+// Retrieve the first calendar in the list using its ID
+$calendarA:=$Office365.calendar.getCalendar($calendarList.calendars[0].id)
+
+```
+
+### Office365.calendar.getCalendars()
+
+**Office365.calendar.getCalendars**({*param*: Object}) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->|Set of options to filter, order, or select specific calendar properties.|
+|result|Object|<-| Object containing the retrieved calendars and related data.|
+
+#### Description
+
+`Office365.calendar.getCalendars()` retrieves a collection of the user's calendars.
+
+In *param*, you can pass the following optional properties:
+
+| Property | Type | Description |
+|---|---|---|
+|select| Text | Specifies which calendar properties (columns) to retrieve. Comma-separated values.|
+|orderby | Text | Specifies the order of the returned results. Syntax is property name followed by `asc` (ascending) or `desc` (descending).|
+|filter| Text | OData filter expression to filter the results. For example: `"name eq 'Work'"` retrieves calendars with the name "Work". |
+
+#### Returned object
+
+The function returns an object containing the following properties:
+
+| Property | Type | Description |
+|---|---|---|
+|calendars| Collection | Collection of calendar objects retrieved from the user's account. Each calendar object contains properties such as `id`, `name`, and `owner`. |
+|statusText| Text | Status message returned by the Microsoft server or the last error message from the 4D error stack.|
+|success| Boolean | `True` if the operation is successful, `False` otherwise.|
+|errors | Collection | Collection of 4D error items (if any):|
+| | | - `.errcode`: 4D error code number.|
+| | | - `.message`: Error description.|
+| | | - `.componentSignature`: Signature of the component that returned the error.|
+
+#### Example
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $Office365 : cs.NetKit.Office365
+var $params; $calendarList : Object
+
+// Set up parameters:
+$params:=New object
+$params.name:="Microsoft"
+$params.permission:="signedIn"
+$params.clientId:="your-client-id" // Replace with your Microsoft identity platform client ID
+$params.redirectURI:="http://127.0.0.1:50993/authorize/"
+$params.scope:="https://graph.microsoft.com/.default"
+
+// Create an OAuth2Provider Object
+$oAuth2:=New OAuth2 provider($params)
+
+// Create an Office365 object
+$Office365:=New Office365 provider($oAuth2)
+
+// Retrieve the entire list of calendars
+$calendarList:=$Office365.calendar.getCalendars()
+
+```
+
+### Office365.calendar.getEvent()
+
+**Office365.calendar.getEvent**(*param*: Object) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->|Object containing the necessary details to retrieve a specific event|
+|result|Object|<-|Object containing the retrieved event|
+
+#### Description
+
+`Office365.calendar.getEvent()` retrieves details of a specific event, including its properties and relationships. The event is identified by its unique `eventId` within the specified calendar.
+
+In *param*, you can pass the following properties:
+
+| Property | Type | Description |
+|---|---|---|
+| eventId | String |(Required) The unique identifier of the event|
+| calendarId | String | Calendar identifier. To retrieve calendar IDs call the calendarList.list(). If not provided, the user's primary (currently logged-in) calendar is used |
+| timeZone | String | Time zone used in the response. UTC by default |
+
+
+#### Returned object
+
+The function returns a Microsoft [`event`](https://learn.microsoft.com/en-us/graph/api/resources/event?view=graph-rest-1.0) object. If the event has attachments, an `attachments` attribute is included, which holds a collection of attachment objects.
+
+### Office365.calendar.getEvents()
+
+**Office365.calendar.getEvents**({*param*: Object}) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->|Object containing filters and options for retrieving calendar events|
+|result|Object|<-| Object containing the retrieved events |
+
+#### Description
+
+`Office365.calendar.getEvents()` retrieves events from a specified calendar. By default, events are pulled from the user's primary calendar unless another calendar is specified.
+
+In *param*, you can pass the following optional properties:
+
+| Property | Type | Description |
+|---|---|---|
+| calendarId | String | Calendar identifier. To retrieve calendar IDs, call Google.Calendar.getCalendars(). If not provided, the user's primary (currently logged-in) calendar is used |
+| startDateTime | Text, Object | Filters events by start time. If set, `endDateTime` must also be provided. **Text:** ISO 8601 UTC timestamp (exclusive upper bound). Defaults to no filtering. **Object:** Must contain `date` (date type) and `time` (time type), formatted according to system settings |
+| endDateTime | Text, Object | Filters events by end time. If set, `startDateTime` must also be provided. **Text:** ISO 8601 UTC timestamp (exclusive lower bound). Defaults to no filtering. **Object:** Must contain `date` (date type) and `time` (time type), formatted according to system settings |
+| timeZone | String | Time zone used in the response. UTC by default|
+| select | Text | Specifies which event properties to return (columns) |
+| orderby | Text | Defines sorting order for results |
+| filter | Text | Applies additional filters based on specific conditions, such as event status, organizer, or categories. Uses [Microsoft Graph OData query syntax](https://learn.microsoft.com/en-us/graph/query-parameters#filter-parameter). Example: "status eq 'confirmed' and organizer/emailAddress/address eq 'example@domain.com'"|
+| top | Integer | Limit number of events per page. Maximum value is 999. If top is not defined, the default value is applied (10). When a result set spans multiple pages. Use `.next()` to fetch additional pages. See [Microsoft’s documentation on paging](https://learn.microsoft.com/en-us/graph/paging) for more information |
+
+**Note**: If no time range is provided (`startDateTime` or `endDateTime`) it returns single-instance meetings and series masters. When both `startDateTime` and `endDateTime` are specified, it retrieves all occurrences, exceptions, and single-instance events within the defined time range.
+
+#### Returned object
+
+The method returns an object containing the following properties:
+
+| Property | Type | Description |
+|---|---|---|
+| errors | Collection | Collection of 4D error items (not returned if an Office 365 server response is received). Each item includes: `[].errcode` (4D error code number), `[].message` (error description), and `[].componentSignature` (component that returned the error) |
+| statusText | Text | Status message returned by the Office 365 server, or the last error message from the 4D error stack |
+|success |Boolean| `True` if the operation is successful, `False` otherwise |
+|isLastPage|Boolean | `True` if the last page of results has been reached |
+| page | Integer | Current page number in the result set. Starts at `1`. The default page size is `10`, but this can be adjusted using the `top` parameter |
+| next() | Function | Retrieves the next page of results and increments `page` by `1`. Returns `True` if successful, `False` if there are no more pages. |
+| previous() | Function | Retrieves the previous page of results and decrements `page` by `1`. Returns `True` if successful, `False` if there are no previous pages |
+| calendarId | String | Calendar identifier, same as the `calendarId` provided in the passed parameters (if present) |
+| events | Collection | Collection of event objects. If any events have attachments, an `attachments` attribute is included, containing a collection of attachment objects |
+
+#### Example
+
+```4d
+// Gets all the calendars
+var $calendars:=$office365.calendar.getCalendars()
+// For the rest of the example, we'll use the first calendar in the list
+var $myCaldendar:=$calendars.calendars[0]
+
+// Gets all the event of the selected calendars
+var $events:=$office365.calendar.getEvents({calendarId: $myCalendar.id; top: 10})
+```
+
+### Office365.calendar.createEvent()
+
+**Office365.calendar.createEvent**(*event*: Object ) : Object
+
+#### Parameters
+
+| Parameter | Type | | Description |
+| --------- | ---- |---|------------------------- |
+| event | Object | -> | Object containing details of the calendar [event](#event-object) to create |
+| result| Object | <- | [Status object](#status-object-microsoft-class)|
+
+#### Description
+
+`Office365.calendar.createEvent()` creates a new calendar event.
+
+In *event*, pass the properties you want to set for the event.
+
+#### Returned Object
+
+The function returns a [**status object**](#status-object-microsoft-class) with an additional `event` property:
+
+| Property | Type | Description|
+| -------- | ---------- | ----------------------------------- |
+| event| Object | [Event object](#event-object) returned by the server |
+| success | Boolean| See [Status object](#status-object-microsoft-class) |
+| statusText | Text| See [Status object](#status-object-microsoft-class) |
+| errors | Collection | See [Status object](#status-object-microsoft-class) |
+
+#### Permissions
+
+One of the following permissions is required to create an event:
+
+| Type | Permission |
+| ----------------------- | --------------------- |
+| Delegated (Work/School) | `Calendars.ReadWrite` |
+| Delegated (Personal) | `Calendars.ReadWrite` |
+| Application | `Calendars.ReadWrite` |
+
+#### Example
+
+Create a calendar event:
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provid
+var $Office365 : cs.NetKit.Office365
+var $event; $result : Object
+
+$event:={}
+$event.subject:="Team Sync"
+$event.start:={date: Current date; time: Current time}
+$event.end:={date: Current date; time: Current time+3600}
+$event.attendees:=[{email: "first.lastname@gmail.com"}]
+
+$result:=$Office365.calendar.createEvent($event)
+If (Not($result.success))
+ ALERT($result.statusText)
+End if
+```
+### Office365.calendar.updateEvent()
+
+**Office365.calendar.updateEvent**(*event*: Object) : Object
+
+#### Parameters
+
+| Parameter | Type | | Description|
+| --------- | ------ | ---- | ----------------------------- |
+| event | Object | ->| Object containing the complete updated [event](#event-object). |
+| Result| Object | <-| [Status object](#status-object-office365-class)|
+
+#### Description
+
+`Office365.calendar.updateEvent()` updates an existing calendar event.
+
+In *event*, pass the event id (mandatory) and the properties you want to update.
+
+To check the updatable properties, please refer to the [event object](#event-object) table below.
+
+> **Note**:
+ When using `Office365.calendar.updateEvent()`, you only need to include the fields you want to update. Any property you leave out will keep its current value, unless it needs to be recalculated due to changes in related fields (e.g., updating recurrence may affect dates).
+
+#### Returned Object
+
+The method returns a [**status object**](#status-object-office365-class) with an additional `event` property:
+
+| Property | Type | Description |
+| ---------- | ---------- | --------------------------------------------------- |
+| event | Object | Updated [event object](#event-object) returned by the server |
+| success | Boolean | [see Status object](#status-object-office365-class) |
+| statusText | Text | [see Status object](#status-object-office365-class) |
+| errors | Collection | [see Status object](#status-object-office365-class) |
+
+#### Permissions
+
+One of the following permissions is required to update an event:
+
+| Type | Permission |
+| ----------------------- | --------------------- |
+| Delegated (Work/School) | `Calendars.ReadWrite` |
+| Delegated (Personal) | `Calendars.ReadWrite` |
+| Application | `Calendars.ReadWrite` |
+
+#### Example
+
+Update an already existing event calendar:
+
+```4d
+#DECLARE($eventId:Text)
+var $oAuth2 : cs.NetKit.OAuth2Provid
+var $Office365 : cs.NetKit.Office365
+var $result : Object
+
+$event.id:=$eventId
+$event.subject:="Updated Meeting Title"
+$event.start:={date: Current date; time: Current time}
+$event.end:={date: Current date; time: Current time+3600}
+
+$result:=$Office365.calendar.updateEvent($event)
+If (Not($result.success))
+ ALERT($result.statusText)
+End if
+```
+
+### Office365.calendar.deleteEvent()
+
+**Office365.calendar.deleteEvent**(*param*: Object) : Object
+
+#### Parameters
+
+| Parameter | Type | Direction | Description |
+| --------- | ------ | --------- | ------------------------------------------------------------------------------------------------------------------- |
+| param | Object | -> | Object containing details for the calendar [event](#event-object) to delete|
+| Result | Object | <- | [Status object](#status-object-office365-class) |
+
+#### Description
+
+`Office365.calendar.deleteEvent()` deletes a calendar event.
+
+In *param*, you can pass the following properties:
+
+| Property | Type | Description |
+| ----------- | ---- | ---------------------------------------- |
+| eventId | Text | **Required.** ID of the event to delete. |
+| calendarId | Text | ID of the calendar. If not provided, the primary calendar of the currently logged-in user is used. |
+
+
+#### Returned Object
+
+The method returns a [**status object**](#status-object-office365-class).
+
+#### Permissions
+
+One of the following permissions is required to delete an event:
+
+| Type | Permission |
+| ----------------------- | --------------------- |
+| Delegated (Work/School) | `Calendars.ReadWrite` |
+| Delegated (Personal) | `Calendars.ReadWrite` |
+| Application | `Calendars.ReadWrite` |
+
+#### Example
+
+Delete a calendar event:
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provid
+var $Office365 : cs.NetKit.Office365
+
+$status:=$office365.calendar.deleteEvent({eventId: $event.event.id})
+If ($result.success)
+ ALERT("Calendar event correctly deleted")
+Else
+ ALERT($result.statusText)
+End if
+
+```
+
+### Office365.category.list()
+
+**Office365.category.list**() : Object
+
+#### Parameters
+
+| Parameter | Type | | Description |
+| --------- | ------ | -- | ------------------------------------------------------------------ |
+| This method does not take any parameters.| | | |
+| Result | Object | <- | [Status object](#status-object) including a `categories` property. |
+
+#### Description
+
+`Office365.category.list()` retrieves the list of defined categories used to group and organize items such as messages and calendar events in a Microsoft account.
+
+Each category includes a display name and a color, which can be applied when creating or updating events.
+
+#### Returned Object
+
+The method returns a [**status object**](#status-object) with an additional `categories` property:
+
+| Property | | Type | Description |
+| --------- | --|-------- | ----------------------------- |
+| success | | Boolean | [See Status object](#status-object) |
+| statusText | | Text | [See Status object](#status-object) |
+| errors | | Collection | [See Status object](#status-object) |
+| categories | | Collection | Collection of category objects. |
+| | id | Text | ID of the category. |
+| | displayName | Text | A unique name that identifies the category in the user's mailbox. Once set, this name cannot be changed. |
+| | color | Text | A pre-set color constant mapped to one of 25 predefined Outlook colors (e.g., `"Preset0"`, `"Preset12"`). See the list of [color constants](https://learn.microsoft.com/en-us/graph/api/resources/outlookcategory?view=graph-rest-1.0#properties) for more details. |
+
+#### Permissions
+
+One of the following permissions is required:
+
+| Type | Permission |
+| ----------------------- | ---------------------- |
+| Delegated (Work/School) | `MailboxSettings.Read` |
+| Delegated (Personal) | `MailboxSettings.Read` |
+| Application | `MailboxSettings.Read` |
+
+#### Example
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provid
+var $Office365 : cs.NetKit.Office365
+var $result : Object
+var $toDisplay : Collection
+
+$result:=$o365.category.list()
+$toDisplay:=[]
+
+If (Not($result.success))
+ ALERT($result.statusText)
+Else
+ // Process the category list
+ For each ($category; $result.categories)
+ $toDisplay.push($category.displayName)
+ End for each
+End if
+```
+### Event object
+
+The `event` object used with Microsoft Calendar methods includes the following main properties. For the full list, refer to the [official Microsoft documentation](https://learn.microsoft.com/en-us/graph/api/resources/event?view=graph-rest-1.0).
+
+| Property | | Type | Description| Updatable |
+| -------- | -- | ----- | ----------------------- |-----------|
+| id | | Text | ID for the event. Case-sensitive and read-only. | |
+| calendarId | | Text | Calendar ID. If not provided, the user's primary calendar is used. | |
+| attachments| | Collection | Event file [attachments](#attachment-object).| |
+| attendees | | Collection | List of attendees.|Yes|
+| | emailAddress | Text | Required. Attendee’s email address.| |
+| | type | Text | Attendee role: `"required"`, `"optional"`, or `"resource"`.| |
+| body | | Object |Body of the message associated with the event.| Yes |
+| | content | Text | Content of the body| |
+| | contentType | Text | Type of the content.
Possible values: `"text"` or `"html"`.| |
+| categories | | Collection | List of categories associated with the event. Must match the `displayName` of categories returned by [`Office365.category.list()`](#office365categorylist). |Yes|
+| start | | Object | Start time of the event.|Yes|
+| | date | Date | Start time of the event. If provided as text, use the format `"yyyy-mm-dd"`.| |
+| | time | Time| Start time (not used for all-day events).| |
+| | dateTime | Text | Combined start date and time (`"yyyy-mm-ddThh:mm:ss"` format). Overrides `date` and `time`. | |
+| | timeZone | Text | Time zone for the `dateTime`, using IANA format (e.g., `"Europe/Zurich"`). Defaults to UTC if not provided.| |
+| end | | Object | End time of the event.|Yes|
+| | date | Date | End date of the event. If provided as text, use the format `"yyyy-mm-dd"`.| |
+| | time | Time | End time (not used for all-day events).| |
+| | dateTime | Text | Combined end date and time (`"yyyy-mm-ddThh:mm:ss"` format). Overrides `date` and `time`.| |
+| | timeZone | Text | Time zone for the `dateTime`, using IANA format (e.g., `"Europe/Zurich"`). Defaults to UTC if not provided.| |
+| isAllDay | | Boolean | (Default: false) Set to `true` if it's an all-day event.|Yes|
+| isDraft | | Boolean |(Default: false) Set to `true` if changes have been made to the meeting but not yet sent to attendees.| |
+| isCancelled | | Boolean | (Default: false) Whether the event is canceled.| |
+| isReminderOn| | Boolean | (Default: false) Whether a reminder is set for the event.| Yes |
+| isOnlineMeeting | | Boolean | (Default: false) Set to `true` to create an online meeting. Once set, the event stays online and further changes to this setting are ignored.|Yes|
+| onlineMeetingProvider | | Text | Provider used: `"teamsForBusiness"`, `"skypeForBusiness"`, `"skypeForConsumer"`, etc.|Yes |
+|location| | Object| Event [location object](https://learn.microsoft.com/en-us/graph/api/resources/location?view=graph-rest-1.0).|Yes |
+| reminderMinutesBeforeStart | | Integer | Time in minutes before start to trigger reminder.|Yes |
+| responseRequested | | Boolean | Whether a response is requested from attendees. Default is `true`.|Yes |
+| recurrence | | Object | [Recurrence pattern](https://learn.microsoft.com/en-us/graph/api/resources/recurrencepattern?view=graph-rest-1.0) (e.g., daily, weekly) .| Yes |
+| seriesMasterId | | Text | ID of the recurring series master event, if this event is part of a recurring series. | |
+| importance | | Text | Event importance: `"low"`, `"normal"`, or `"high"`.|Yes |
+| sensitivity | | Text | Event sensitivity: `"normal"`, `"personal"`, `"private"`, `"confidential"`.| Yes |
+| showAs| | Text | Availability status: `busy`, `free`, etc.| Yes |
+| subject | | Text | Event title or subject line.| Yes |
+| allowNewTimeProposals | | Boolean | (Default: true) Whether attendees can propose a new time.| |
+| hideAttendees | | Boolean | (Default: false) If `true`, attendees will only see themselves.|Yes|
+
+## Mail
+
+### Office365.mail.append()
+
+**Office365.mail.append**( *email* : Object ; *folderId* : Text) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|email|Object|->| Microsoft message object to append|
+|folderId|Text|->| Id of the destination folder. Can be a folder id or a [Well-known folder name](#well-known-folder-name).|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.append()` creates a draft *email* in the *folderId* folder.
+
+In `email`, pass the email to create. It must be of the [Microsoft mail object](#microsoft-mail-object-properties) type.
+
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadWrite|
+|Application|Mail.ReadWrite|
+
+
+#### Example
+
+```4d
+$status:=$office365.mail.append($draft; $folder.id)
+```
+
+
+### Office365.mail.copy()
+
+**Office365.mail.copy**( *mailId* : Text ; *folderId* : Text) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailId|Text|->| Id of the mail to copy|
+|folderId|Text|->| Id of the destination folder. Can be a folder id or a [Well-known folder name](#well-known-folder-name).|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.copy()` copies the *mailId* email to the *folderId* folder within the user's mailbox.
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadWrite|
+|Application|Mail.ReadWrite|
+
+#### Example
+
+To copy an email from a folder to another:
+
+```4d
+$status:=$office365.mail.copy($mailId; $folderId)
+```
+
+
+### Office365.mail.createFolder()
+
+**Office365.mail.createFolder**( *name* : Text { ; *isHidden* : Boolean { ; *parentFolderId* : Text } }) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|name|Text|->|Display name of the new folder|
+|isHidden|Boolean|->|True to create a hidden folder (Default is False)|
+|parentFolderId|Text|->|ID of the parent folder to get. Can be a folder id or a [Well-known folder name](#well-known-folder-name).|
+|Result|Object|<-| [Status object](#status-object) |
+
+`Office365.mail.getFolder()` creates a new folder named *name* and returns its ID in the [status object](#status-object).
+
+By default, the new folder is not hidden. Pass `True` in the isHidden parameter to create a hidden folder. This property cannot be changed afterwards. Find more information in [Hidden mail folders](https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0#hidden-mail-folders) on the Microsoft web site.
+
+By default, the new folder is created at the root folder of the mailbox. If you want to create it within an existing folder, pass its id in the *parentFolderId* parameter.
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadWrite|
+|Application|Mail.ReadWrite|
+
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+#### Example
+
+You want to create a "Backup" mail folder at the root of your mailbox and move an email to this folder:
+
+```4d
+// Creates a new folder on the root
+$status:=$office365.mail.createFolder("Backup")
+If($status.success=True)
+ $folderId:=$status.id
+ // Moves your email in the new folder
+ $status:=$office365.mail.move($mailId; $folderId)
+End if
+```
+
+### Office365.mail.delete()
+
+**Office365.mail.delete**( *mailId* : Text ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailId|Text|->| Id of the mail to delete|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.delete()` deletes the *mailId* email.
+
+
+#### Permissions
+
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadWrite|
+|Application|Mail.ReadWrite|
+
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+
+**Note:** You may not be able to delete items in the recoverable items deletions folder (for more information, see the [Microsoft's documentation website](https://learn.microsoft.com/en-us/graph/api/message-delete?view=graph-rest-1.0&tabs=http)).
+
+
+#### Example
+
+You want to delete all mails in the *$mails* collection:
+
+```4d
+For each($mail;$mails)
+ $office365.mail.delete($mail.id)
+End for each
+```
+
+### Office365.mail.deleteFolder()
+
+**Office365.mail.deleteFolder**( *folderId* : Text ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|folderId|Text|->| ID of the folder to delete. Can be a folder id or a [Well-known folder name](#well-known-folder-name) if one exists.|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.deleteFolder()` deletes the *folderId* mail folder.
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadWrite|
+|Application|Mail.ReadWrite|
+
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+#### Example
+
+```4d
+$status:=$office365.mail.deleteFolder($folderId)
+```
+
+### Office365.mail.getFolder()
+
+**Office365.mail.getFolder**( *folderId* : Text ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|folderId|Text|->|ID of the folder to get. Can be a folder ID or a [Well-known folder name](#well-known-folder-name).|
+|Result|Object|<-|mailFolder object|
+
+`Office365.mail.getFolder()` allows you to get a **mailFolder** object from its *folderId*.
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
+|Application|Mail.ReadBasic.All, Mail.Read, Mail.ReadWrite|
+
+
+#### mailFolder object
+
+The method returns a **mailFolder** object containing the following properties (additional information can be returned by the server):
+
+| Property | Type | Description |
+|---|---|---|
+|childFolderCount|Integer|Number of immediate child mailFolders in the current mailFolder|
+|displayName|Text|mailFolder's display name|
+|id|Text|mailFolder's unique identifier|
+|isHidden|Boolean|Indicates whether the mailFolder is hidden. This property can be set only when creating the folder. Find more information in [Hidden mail folders](https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0#hidden-mail-folders) on the Microsoft web site.|
+|parentFolderId|Text|Unique identifier for the mailFolder's parent mailFolder.|
+|totalItemCount|Integer|Number of items in the mailFolder.|
+|unreadItemCount|Integer|Number of items in the mailFolder marked as unread.|
+
+
+### Office365.mail.getFolderList()
+
+**Office365.mail.getFolderList**( *param* : Object ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->| Description of folders to get|
+|Result|Object|<-| Status object that contains folder list and other information|
+
+`Office365.mail.getFolderList()` allows you to get a mail folder collection of the signed-in user.
+
+In *param*, pass an object to define the folders to get. The available properties for that object are (all properties are optional):
+
+| Property | Type | Description |
+|---|---|---|
+|folderId|text|Can be a folder id or a [Well-known folder name](#well-known-folder-name).
- If it is a parent folder id, get the folder collection under the specified folder (children folders
- If the property is omitted or its value is "", get the mail folder collection directly under the root folder.|
+|search|text|Restricts the results of a request to match a search criterion. The search syntax rules are available on [Microsoft's documentation website](https://docs.microsoft.com/en-us/graph/search-query-parameter#using-search-on-directory-object-collections).|
+|filter|text|Allows retrieving just a subset of folders. See [Microsoft's documentation on filter parameter](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter).|
+|select|text|Set of properties to retrieve. Each property must be separated by a comma (,). |
+|top|integer|Defines the page size for a request. Maximum value is 999. If `top` is not defined, default value is applied (10). When a result set spans multiple pages, you can use the `.next()` function to ask for the next page. See [Microsoft's documentation on paging](https://docs.microsoft.com/en-us/graph/paging) for more information. |
+|orderBy|text|Defines how returned items are ordered. Default is ascending order. Syntax: "fieldname asc" or "fieldname desc" (replace "fieldname" with the name of the field to be arranged).|
+|includeHiddenFolders|boolean|True to include hidden folders in the response. False (default) to not return hidden folders. |
+
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
+|Application|Mail.ReadBasic.All, Mail.Read, Mail.ReadWrite|
+
+
+#### Returned object
+
+The method returns a status object containing the following properties:
+
+| Property || Type | Description |
+|---|---| ---|---|
+| errors | | Collection | Collection of 4D error items (not returned if an Office 365 server response is received)|
+||[].errcode|Integer|4D error code number|
+||[].message|Text|Description of the 4D error|
+||[].componentSignature|Text|Signature of the internal component that returned the error|
+| isLastPage | | Boolean | `True` if the last page is reached |
+| page || Integer | Folder information page number. Starts at 1. By default, each page holds 10 results. Page size limit can be set in the `top` option. |
+| next() || Function | Function that updates the `folders` collection with the next mail information page and increases the `page` property by 1. Returns a boolean value:
- If a next page is successfully loaded, returns `True`
- If no next page is returned, the `folders` collection is not updated and `False` is returned |
+| previous() || Function | Function that updates the `folders` collection with the previous folder information page and decreases the `page` property by 1. Returns a boolean value:
- If a previous page is successfully loaded, returns `True`
- If no previous `page` is returned, the `folders` collection is not updated and `False` is returned |
+| statusText || Text | Status message returned by the Office 365 server, or last error returned in the 4D error stack |
+| success | | Boolean | `True` if the `Office365.mail.getFolderList()` call is successful, `False` otherwise |
+| folders || Collection | Collection of `mailFolder` objects with information on folders.|
+|| [].childFolderCount|Integer|The number of immediate child mailFolders in the current mailFolder.
+|| [].displayName| Text| The mailFolder's display name.
+|| [].id| Text| The mailFolder's unique identifier.
+|| [].isHidden| Boolean| Indicates whether the mailFolder is hidden. This property can be set only when creating the folder. Find more information in Hidden mail folders.
+|| [].parentFolderId| Text| The unique identifier for the mailFolder's parent mailFolder.
+|| [].totalItemCount |Integer| The number of items in the mailFolder.
+|| [].unreadItemCount| Integer |The number of items in the mailFolder marked as unread.
+
+
+The method returns an empty collection in the `folders` property if:
+- no folders are found at the defined location
+- an error is thrown
+
+#### Example
+
+You want to :
+
+```4d
+//get the mail folder collection under the root folder (in $result.folders)
+var $result : Object
+$result:=$office365.mail.getFolderList()
+
+//get the mail subfolder collection under the 9th folder
+var $subfolders : Collection
+$subfolders:=$office365.mail.getFolderList($result.folders[8].id)
+```
+
+
+### Office365.mail.getMail()
+
+**Office365.mail.getMail**( *mailId* : Text { ; *param* : Object } ) : Object
**Office365.mail.getMail**( *mailId* : Text { ; *param* : Object } ) : Blob
+
+#### Parameters
+
+|Parameter||Type||Description|
+|-----|----|--- |:---:|------|
+|mailId||Text|->| Id of the mail to get|
+|param||Object|->|Format options for the returned mail object|
+||mailType|Text|| Type of the mail object to return. Available values:
- "MIME"
- "JMAP"
- "Microsoft" (default)
By default if omitted, the same format as the [`mail.type` property](#new-office365-provider) is used|
+||contentType|Text|| Format of the `body` and `uniqueBody` properties to be returned. Available values:
- "text"
- "html" (default)|
+|Result||Blob | Object|<-| Downloaded mail|
+
+`Office365.mail.getMail()` allows you to get a single mail from its *mailId*.
+
+By default, the mail is returned with its original format, as defined in the [`mail.type` property of the provider](#new-office365-provider). However, you can convert it to any type using the `mailType` property of the *param* parameter.
+
+You can also select the preferred format of the `body` and `uniqueBody` properties of the returned mail using the `contentType` property of the *param* parameter.
+
+The data type of the function result depends on the mail type:
+
+|Mail type|Function result|
+|---|---|
+|MIME|Blob|
+|JMAP|Object|
+|Microsoft|Object|
+
+If an error occurs, the function returns Null and an error is generated.
+
+See also [Microsoft's documentation website](https://learn.microsoft.com/en-us/graph/api/message-get?view=graph-rest-1.0&tabs=http).
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadBasic, Mail.Read|
+|Delegated (personal Microsoft account)|Mail.ReadBasic, Mail.Read|
+|Application|Mail.ReadBasic.All, Mail.Read|
+
+
+#### Example
+
+Download a specific email:
+
+```4d
+$mail:=$office.mail.getMail($mailId)
+```
+
+
+### Office365.mail.getMails()
+
+**Office365.mail.getMails**( *param* : Object ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->| Description of mails to get|
+|Result|Object|<-| Status object that contains mail list and other information|
+
+`Office365.mail.getMails()` allows you to get messages in the signed-in user's mailbox (for detailed information, please refer to the [Microsoft's documentation website](https://learn.microsoft.com/en-us/graph/api/user-list-messages?view=graph-rest-1.0&tabs=http)).
+
+This method returns mail bodies in HTML format only.
+
+In *param*, pass an object to define the mails to get. The available properties for that object are (all properties are optional):
+
+| Property | Type | Description |
+|---|---|---|
+|folderId|text|To get messages in a specific folder. Can be a folder id or a [Well-known folder name](#well-known-folder-name). If the destination folder is not present or empty, get all the messages in a user's mailbox.|
+|search|text|Restricts the results of a request to match a search criterion. The search syntax rules are available on [Microsoft's documentation website](https://learn.microsoft.com/en-us/graph/search-query-parameter?tabs=http#using-search-on-message-collections).|
+|filter|text|Allows retrieving just a subset of mails. See [Microsoft's documentation on filter parameter](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter).|
+|select|text|Set of [properties of the Microsoft Mail object](#microsoft-mail-object-properties) to retrieve. Each property must be separated by a comma (,). |
+|top|integer|Defines the page size for a request. Maximum value is 999. If `top` is not defined, default value is applied (10). When a result set spans multiple pages, you can use the `.next()` function to ask for the next page. See [Microsoft's documentation on paging](https://docs.microsoft.com/en-us/graph/paging) for more information. |
+|orderBy|text|Defines how returned items are ordered. Default is ascending order. Syntax: "fieldname asc" or "fieldname desc" (replace "fieldname" with the name of the field to be arranged).|
+
+
+#### Returned object
+
+The method returns a status object containing the following properties:
+
+| Property || Type | Description |
+|---|---| ---|---|
+| errors | | Collection | Collection of 4D error items (not returned if an Office 365 server response is received)|
+||[].errcode|Integer|4D error code number|
+||[].message|Text|Description of the 4D error|
+||[].componentSignature|Text|Signature of the internal component that returned the error|
+| isLastPage | | Boolean | `True` if the last page is reached |
+| page || Integer | Mail information page number. Starts at 1. By default, each page holds 10 results. Page size limit can be set in the `top` option. |
+| next() || Function | Function that updates the `mails` collection with the next mail information page and increases the `page` property by 1. Returns a boolean value:
- If a next page is successfully loaded, returns `True`
- If no next page is returned, the `mails` collection is not updated and `False` is returned |
+| previous() || Function | Function that updates the `folders` collection with the previous mail information page and decreases the `page` property by 1. Returns a boolean value:
- If a previous page is successfully loaded, returns `True`
- If no previous `page` is returned, the `mails` collection is not updated and `False` is returned |
+| statusText || Text | Status message returned by the Office 365 server, or last error returned in the 4D error stack |
+| success | | Boolean | `True` if the `Office365.mail.getFolderList()` call is successful, `False` otherwise |
+| mails || Collection | Collection of [Microsoft mail objects](#microsoft-mail-object-properties). If no mail is returned, the collection is empty|
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadBasic, Mail.Read, Mail.ReadWrite|
+|Application|Mail.ReadBasic.All, Mail.Read, Mail.ReadWrite|
+
+#### Example
+
+You want to retrieve *sender* and *subject* properties of all the mails present in the Inbox folder, using its [well-known folder name](#well-known-folder-name):
+
+```4d
+$param:=New object
+$param.folderId:="inbox"
+$param.select:="sender,subject"
+
+$mails:=$office365.mail.getMails($param)
+```
+
+### Office365.mail.move()
+
+**Office365.mail.move**( *mailId* : Text ; *folderId* : Text) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailId|Text|->| Id of the mail to move|
+|folderId|Text|->| Id of the destination folder. Can be a folder id or a [Well-known folder name](#well-known-folder-name).|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.move()` moves the *mailId* email to the *folderId* folder. It actually creates a new copy of the email in the destination folder and removes the original email from its source folder.
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadWrite|
+|Application|Mail.ReadWrite|
+
+
+#### Example
+
+To move an email from a folder to another:
+
+```4d
+$status:=$office365.mail.move($mailId; $folderId)
+```
+
+### Office365.mail.renameFolder()
+
+**Office365.mail.renameFolder**( *folderId* : Text ; *name* : Text ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|folderId|Text|->| ID of the folder to rename|
+|name|Text|->| New display name for the folder|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.renameFolder()` renames the *folderId* mail folder with the provided *name*.
+
+Note that the renamed folder ID is different from the *folderId*. You can get it in the returned [status object](#status-object).
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.ReadWrite|
+|Delegated (personal Microsoft account)|Mail.ReadWrite|
+|Application|Mail.ReadWrite|
+
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+#### Example
+
+You want to rename the the "Backup" folder to "Backup_old":
+
+```4d
+$status:=$office365.mail.renameFolder($folderId; "Backup_old")
+```
+
+
+### Office365.mail.reply()
+
+**Office365.mail.reply**( *reply* : Object ; *mailId* : Text { ; *replyAll* : Boolean } ) : Object
+
+#### Parameters
+
+|Parameter||Type||Description|
+|----|-----|--- |:---:|------|
+|reply||Object|->| reply object|
+||message|Text | Blob | Object|->|Microsoft message (object) or JMAP (object) or MIME (Blob / Text) that contains the reponse|
+||comment|Text|->| (only available with Microsoft message object or no message) Message used as body to reply to the email when present. You must specify either a *comment* or the [body property](#microsoft-mail-object-properties) of the message parameter; specifying both will return an HTTP 400 Bad Request error.|
+|mailId||Text|->| Id of the mail to which you reply|
+|replyAll||Boolean|->| True to reply to all recipients of the message. Default=False|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.reply()` replies to the sender of *mailId* message and, optionnally, to all recipients of the message.
+
+**Note:** Some mails, like drafts, cannot be replied.
+
+If you pass `False` in *replyAll* and if the original message specifies a recipient in the `replyTo` property, the reply is sent to the recipients in `replyTo` and not to the recipient in the `from` property.
+
+#### Returned object
+
+The method returns a [status object](#status-object)
+
+#### Permissions
+
+One of the following permissions is required to call this API. For more information, including how to choose permissions, see the [Permissions section on the Microsoft documentation](https://docs.microsoft.com/en-us/graph/permissions-reference).
+
+|Permission type|Permissions (from least to most privileged)
+|---|----|
+|Delegated (work or school account)|Mail.Send|
+|Delegated (personal Microsoft account)|Mail.Send|
+|Application|Mail.Send|
+
+#### Example
+
+To reply to an email and create a conversation:
+
+```4d
+$reply:=New object
+// Text that will be send as reply
+$reply.comment:="Thank you for your message"
+$status:=$office365.mail.reply($reply; $mails.mailId)
+```
+
+
+
+### Office365.mail.send()
+
+**Office365.mail.send**( *email* : Text ) : Object
**Office365.mail.send**( *email* : Object ) : Object
**Office365.mail.send**( *email* : Blob ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|email|Text | Blob | Object|->| Email to be sent|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.send()` sends an email using the MIME or JSON formats.
+
+In `email`, pass the email to be sent. Possible types:
+
+* Text or Blob: the email is sent using the MIME format
+* Object: the email is sent using the JSON format, in accordance with either:
+ * the [Microsoft mail object properties](#microsoft-mail-object-properties)
+ * the [4D email object format](https://developer.4d.com/docs/API/EmailObjectClass.html#email-object), which follows the JMAP specification
+
+> Passing both the `textBody` and `htmlBody` properties is not supported by `Office365.mail.send()`. In this case, only the html body part is actually sent.
+
+The data type passed in `email` must be compatible with the [`Office365.mail.type` property](#returned-object-1). In the following example, since the mail type is `Microsoft`, `$email` must be an object. For the list of available properties, see [Microsoft mail object's properties](#microsoft-mail-object-properties):
+
+```4d
+$Office365:=New Office365 provider($token; New object("mailType"; "Microsoft"))
+$status:=$Office365.mail.send($email)
+```
+
+> To avoid authentication errors, make sure your application asks for permission to send emails through the Microsoft Graph API. See [Permissions](https://docs.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http#permissions).
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+### Office365.mail.update()
+
+**Office365.mail.update**( *mailId* : Text ; *updatedFields* : Object ) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|mailId|Text|->|The ID of the email to update|
+|updatedFields|Object|->|email fields to update|
+|Result|Object|<-| [Status object](#status-object) |
+
+#### Description
+
+`Office365.mail.update()` allows you to update various properties of received or drafted emails.
+
+In *updatedFields*, you can pass several properties:
+
+|Property|Type|Description|Updatable only if isDraft = true|
+|:----|:----|:----|:----|
+|bccRecipients|Recipient|The Bcc recipients for the message.| |
+|body|ItemBody|The body of the message.|X|
+|categories|String collection|The categories associated with the message.| |
+|ccRecipients|Recipient collection|The Cc recipients for the message.| |
+|flag|followupFlag|The flag value that indicates the status, start date, due date, or completion date for the message.| |
+|from|Recipient|The mailbox owner and sender of the message. Must correspond to the actual mailbox used.|X|
+|importance|String|The importance of the message. The possible values are: Low, Normal, High.| |
+|inferenceClassification|String|The classification of the message for the user, based on inferred relevance or importance, or on an explicit override. The possible values are: focused or other.| |
+|internetMessageId|String|The message ID in the format specified by RFC2822.|X|
+|isDeliveryReceiptRequested|Boolean|Indicates whether a read receipt is requested for the message.|X|
+|isRead|Boolean|Indicates whether the message has been read.| |
+|isReadReceiptRequested|Boolean|Indicates whether a read receipt is requested for the message.|X|
+|replyTo|Recipient collection|The email addresses to use when replying.|X|
+|sender|Recipient|The account that is actually used to generate the message. Updatable when sending a message from a shared mailbox, or sending a message as a delegate. In any case, the value must correspond to the actual mailbox used.|X|
+|subject|String|The subject of the message.|X|
+|toRecipients|Recipient collection|The To recipients for the message.| |
+
+**Notes:**
+
+* Existing properties that are not included in the *updatedFields* object will maintain their previous values or be recalculated based on changes to other property values.
+* Specific properties, such as the body or subject, can only be updated for emails in draft status (isDraft = true).
+
+#### Returned object
+
+The method returns a [status object](#status-object).
+
+### Well-known folder names
+
+
+Outlook creates certain folders for users by default. Instead of using the corresponding `folder id` value, for convenience, you can use the well-known folder name when accessing these folders. Well-known names work regardless of the locale of the user's mailbox. For example, you can get the Drafts folder using its well-known name "draft". For more information, please refer to the [Microsoft Office documentation](https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0).
+
+
+### "Microsoft" mail object properties
+
+When you send an email with the "Microsoft" mail type, you must pass an object to `Office365.mail.send()`. For a comprehensive list of properties supported by Microsoft mail objects, please refer to the [Microsoft Office documentation](https://learn.microsoft.com/en-us/graph/api/resources/message?view=graph-rest-1.0#properties). Most common properties are listed below:
+
+| Property | Type | Description |
+|---|---|---|
+| attachments |[attachment](#attachment-object) collection | The attachments for the email. |
+| bccRecipients |[recipient](#recipient-object) collection | The Bcc: recipients for the message. |
+| body |itemBody object| The body of the message. It can be in HTML or text format.|
+| ccRecipients |[recipient](#recipient-object) collection | The Cc: recipients for the message. |
+| flag |[followup flag](#followup-flag-object) object| The flag value that indicates the status, start date, due date, or completion date for the message. |
+| from |[recipient](#recipient-object) object | The owner of the mailbox from which the message is sent. In most cases, this value is the same as the sender property, except for sharing or delegation scenarios. The value must correspond to the actual mailbox used.|
+| id |Text|Unique identifier for the message (note that this value may change if a message is moved or altered).|
+| importance|Text| The importance of the message. The possible values are: `low`, `normal`, and `high`.|
+| internetMessageHeaders |[internetMessageHeader](#internetmessageheader-object) collection | A collection of message headers defined by [RFC5322](https://www.ietf.org/rfc/rfc5322.txt). The set includes message headers indicating the network path taken by a message from the sender to the recipient.|
+| isDeliveryReceiptRequested |Boolean| Indicates whether a delivery receipt is requested for the message. |
+| isReadReceiptRequested |Boolean| Indicates whether a read receipt is requested for the message. |
+| replyTo |[recipient](#recipient-object) collection | The email addresses to use when replying. |
+| sender |[recipient](#recipient-object) object | The account that is actually used to generate the message. In most cases, this value is the same as the from property. You can set this property to a different value when sending a message from a shared mailbox, for a shared calendar, or as a delegate. In any case, the value must correspond to the actual mailbox used. Find out more about setting the [from and sender properties](https://docs.microsoft.com/en-us/graph/outlook-create-send-messages#setting-the-from-and-sender-properties) of a message. |
+| subject |Text| The subject of the message.|
+| toRecipients |[recipient](#recipient-object) collection | The To: recipients for the message. |
+
+#### Attachment object
+
+| Property | Type | Description |
+|---|---|---|
+|@odata.type|Text|always "#microsoft.graph.fileAttachment" (note that the property name requires that you use the `[""]` syntax)|
+|contentBytes|Text| The base64-encoded contents of the file (only to send mails) |
+|contentId| Text| The ID of the attachment in the Exchange store.|
+|contentType |Text| The content type of the attachment.|
+|id |Text|The attachment ID. (cid)|
+|isInline |Boolean |Set to true if this is an inline attachment.|
+|name| Text| The name representing the text that is displayed below the icon representing the embedded attachment.This does not need to be the actual file name.|
+|size|Number|The size in bytes of the attachment.|
+|getContent()|Function|Returns the contents of the attachment object in a `4D.Blob` object.|
+
+#### itemBody object
+
+| Property | Type | Description | Can be null of undefined |
+|---|---|---|---|
+|content|Text|The content of the item.|No|
+|contentType|Text| The type of the content. Possible values are `"text"` and `"html"` |No|
+
+#### recipient object
+
+| Property || Type | Description | Can be null of undefined |
+|---|---|---|---|---|
+|emailAddress||Object||Yes|
+||address|Text|The email address of the person or entity.|No|
+||name|Text| The display name of the person or entity.|Yes|
+
+#### internetMessageHeader object
+
+| Property | Type | Description | Can be null of undefined |
+|---|---|---|---|
+|name | Text|Represents the key in a key-value pair.|No|
+|value|Text|The value in a key-value pair.|No|
+
+#### followup flag object
+
+| Property | Type | Description |
+|---|---|---|
+|dueDateTime|[dateTime | TimeZone](#datetime-and-timezone)| The date and time that the follow up is to be finished. Note: To set the due date, you must also specify the `startDateTime`; otherwise, you will get a `400 Bad Request` response.|
+|flagStatus|Text|The status for follow-up for an item. Possible values are `"notFlagged"`, `"complete"`, and `"flagged"`.|
+|startDateTime|[dateTime | TimeZone](#datetime-and-timezone)| The date and time that the follow-up is to begin.|
+
+#### dateTime and TimeZone
+
+|Property|Type|Description|
+|---|---|---|
+|dateTime|Text|A single point of time in a combined date and time representation ({date}T{time}; for example, 2017-08-29T04:00:00.0000000).|
+|timeZone|Text|Represents a time zone, for example, "Pacific Standard Time". See below for more possible values.|
+
+In general, the timeZone property can be set to any of the time zones currently supported by Windows, as well as the additional time zones supported by the calendar API:
+* [Default time zones](https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones?view=windows-11)
+* [Additional time zones](https://docs.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0#additional-time-zones)
+
+
+
+#### Example: Create an email with a file attachment and send it
+
+Send an email with an attachment, on behalf of a Microsoft 365 user:
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $token; $param; $email; $status : Object
+
+// Set up authentication
+$param:=New object()
+$param.name:="Microsoft"
+$param.permission:="signedIn"
+$param.clientId:="your-client-id" // Replace with your client ID
+$param.redirectURI:="http://127.0.0.1:50993/authorize/"
+// Ask permission to send emails on behalf of the Microsoft user
+$param.scope:="https://graph.microsoft.com/Mail.Send"
+
+$oAuth2:=New OAuth2 provider($param)
+
+$token:=$oAuth2.getToken()
+
+// Create the email, specify the sender and the recipient
+$email:=New object()
+$email.from:=New object("emailAddress"; New object("address"; "senderAddress@hotmail.fr")) // Replace with sender's email
+$email.toRecipients:=New collection(New object("emailAddress"; New object("address"; "recipientAddress@hotmail.fr")))
+$email.body:=New object()
+$email.body.content:="Hello, World!"
+$email.body.contentType:="html"
+$email.subject:="Hello, World!"
+
+// Create a text file and attach it to the email
+var $attachment : Object
+var $attachmentText : Text
+
+$attachmentText:="Simple text file"
+BASE64 ENCODE($attachmentText)
+$attachment:=New object
+$attachment["@odata.type"]:="#microsoft.graph.fileAttachment"
+$attachment.name:="attachment.txt"
+$attachment.contentBytes:=$attachmentText
+$email.attachments:=New collection($attachment)
+
+// Send the email
+$Office365:=New Office365 provider($token; New object("mailType"; "Microsoft"))
+$status:=$Office365.mail.send($email)
+```
+
+## Status object
+
+Several Office365.mail functions return a standard `**status object**`, containing the following properties:
+
+|Property|Type|Description|
+|---------|--- |------|
+|success|Boolean| True if the operation was successful|
+|statusText|Text| Status message returned by the server or last error returned by the 4D error stack|
+|errors|Collection| Collection of errors. Not returned if the server returns a `statusText`|
+|id|Text|
- [`copy()`](#office365-mail-copy) and [`move()`](#office365-mail-move): returned id of the mail.
- [`createFolder()`](#office365-mail-createFolder) and [`renameFolder()`](#office365-mail-renameFolder): returned id of the folder|
+
+
+Basically, you can test the `success` and `statusText` properties of this object to know if the function was correctly executed.
+
+#### Error handling
+
+When an error occurs during the execution of an Office365.mail function:
+
+- if the function returns a [`**status object**`](#status-object), the error is handled by the status object and no error is thrown,
+- if the function does not return a **status object**, an error is thrown that you can intercept with a project method installed with `ON ERR CALL`.
+
+## User
+
+### Office365.user.get()
+
+**Office365.user.get**( *id* : Text { ; *select* : Text }) : Object
**Office365.user.get**( *userPrincipalName* : Text { ; *select* : Text }) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|id|Text|->| Unique identifier of the user to search for |
+|userPrincipalName|Text|->| User principal name (UPN) of the user to search for|
+|select|Text|->| Set of properties to be returned|
+|Result|Object|<-| Object holding information on the user|
+
+#### Description
+
+`Office365.user.get()` returns information on the user whose ID matches the *id* parameter, or whose User Principal Name (UPN) matches the *userPrincipalName* parameter.
+
+
+> The UPN is an Internet-style login name for the user based on the Internet standard RFC 822. By convention, it should correspond to the user's email name.
+
+If the ID or UPN is not found or connection fails, the command returns an object with `Null` as a value and throws an error.
+
+In *select*, you can pass a string that contains a specific set of properties you want to retrieve. Each property must be separated by a comma (,). If the select parameter is omitted, the function returns an object with a predefined set of properties (see below).
+
+> The list of available properties is available on [Microsoft's documentation website](https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0).
+
+#### Returned object
+
+By default, if the *select* parameter is omitted, the command returns an object with the following properties:
+
+| Property | Type | Description
+|---|---|---|
+id | Text | Unique identifier for the user
+businessPhones | Collection | The user's phone numbers.
+displayName | Text | Name displayed in the address book for the user.|
+givenName | Text | The user's first name.
+jobTitle | Text | The user's job title.
+mail | Text | The user's email address.
+mobilePhone | Text | The user's cellphone number.
+officeLocation | Text | The user's physical office location.
+preferredLanguage | Text | The user's language of preference.
+surname | Text | The user's last name.
+userPrincipalName | Text | The user's principal name.
+
+Otherwise, the object contains the properties specified in the `select` parameter.
+
+For more details on user information, see [Microsoft's documentation website](https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0).
+
+### Office365.user.getCurrent()
+
+**Office365.user.getCurrent**({*select* : Text}) : Object
+
+#### Description
+
+`Office365.user.getCurrent()` returns information on the current user. In this case, it requires a [signed-in user](https://docs.microsoft.com/en-us/graph/auth-v2-user), and therefore a delegated permission.
+
+The command returns a `Null` object if the session is not a sign-in session.
+
+In *select*, pass a string that contains a set of properties you want to retrieve. Each property must be separated by a comma (,).
+
+By default, if the *select* parameter is not defined, the command returns an object with a default set of properties (see the [property table](#returned-object)).
+
+#### Example
+
+To retrieve information from the current user:
+
+```4d
+var $userInfo; $params : Object
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $Office365 : cs.NetKit.Office365
+
+// Set up parameters:
+$params:=New object
+$params.name:="Microsoft"
+$params.permission:="signedIn"
+$params.clientId:="your-client-id" // Replace with your Microsoft identity platform client ID
+$params.redirectURI:="http://127.0.0.1:50993/authorize/"
+$param.scope:="https://graph.microsoft.com/.default"
+
+$oAuth2:=New Oauth2 provider($params) //Creates an OAuth2Provider Object
+
+$Office365:=New Office365 provider($oAuth2) // Creates an Office365 object
+
+// Return the properties specified in the parameter.
+$userInfo:=$Office365.user.getCurrent("id,userPrincipalName,\
+principalName,displayName,givenName,mail")
+```
+
+### Office365.user.list()
+
+**Office365.user.list**({*param*: Object}) : Object
+
+#### Parameters
+
+|Parameter|Type||Description|
+|---------|--- |:---:|------|
+|param|Object|->| Additional options for the search|
+|result|Object|<-| Object holding a collection of users and additional info on the request|
+
+#### Description
+
+`Office365.user.list()` returns a list of Office365 users.
+
+In *param*, you can pass an object to specify additional search options. The following table groups the available search options:
+
+| Property | Type | Description |
+|---|---|---|
+| search | Text | Restricts the results of a request to match a search criterion. The search syntax rules are available on [Microsoft's documentation website](https://docs.microsoft.com/en-us/graph/search-query-parameter#using-search-on-directory-object-collections).|
+| filter | Text | Allows retrieving just a subset of users. See [Microsoft's documentation on filter parameter](https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter). |
+| select | Text | Set of properties to retrieve. Each property must be separated by a comma (,). By default, if `select` is not defined, the returned user objects have a [default set of properties](#returned-object)|
+|top| Integer | Defines the page size for a request. Maximum value is 999. If `top` is not defined, the default value is applied (100). When a result set spans multiple pages, you can use the `.next()` function to ask for the next page. See [Microsoft's documentation on paging](https://docs.microsoft.com/en-us/graph/paging) for more information. |
+|orderBy| Text | Defines how the returned items are ordered. By default, they are arranged in ascending order. The syntax is "fieldname asc" or "fieldname desc". Replace "fieldname" with the name of the field to be arranged. |
+
+#### Returned object
+
+The returned object holds a collection of users as well as status properties and functions that allow you to navigate between different pages of results.
+
+By default, each user object in the collection has the [default set of properties listed in the `Office365.user.get()` function](#returned-object). This set of properties can be customized using the `select` parameter.
+
+| Property | Type | Description |
+|---|---|---|
+| errors | Collection | Collection of 4D error items (not returned if an Office 365 server response is received):
- [].errcode is the 4D error code number
- [].message is a description of the 4D error
- [].componentSignature is the signature of the internal component that returned the error|
+| isLastPage | Boolean | `True` if the last page is reached |
+| page | Integer | User information page number. Starts at 1. By default, each page holds 100 results. Page size limit can be set in the `top` option. |
+| next() | Function | Function that updates the `users` collection with the next user information page and increases the `page` property by 1. Returns a boolean value:
- If a next page is successfully loaded, returns `True`
- If no next page is returned, the `users` collection is not updated and `False` is returned |
+| previous() | Function | Function that updates the `users` collection with the previous user information page and decreases the `page` property by 1. Returns a boolean value:
- If a previous page is successfully loaded, returns `True`
- If no previous `page` is returned, the `users` collection is not updated and `False` is returned |
+| statusText | Text | Status message returned by the Office 365 server, or last error returned in the 4D error stack |
+| success | Boolean | `True` if the `Office365.user.list()` operation is successful, `False` otherwise |
+| users | Collection | Collection of objects with information on users.|
+
+
+#### Example
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $Office365 : cs.NetKit.Office365
+var $userInfo; $params; $userList; $userList2; $userList3; $userList4 : Object
+var $col : Collection
+
+// Set up parameters:
+$params:=New object
+$params.name:="Microsoft"
+$params.permission:="signedIn"
+$params.clientId:="your-client-id" // Replace with your Microsoft identity platform client ID
+$params.redirectURI:="http://127.0.0.1:50993/authorize/"
+$params.scope:="https://graph.microsoft.com/.default"
+
+// Create an OAuth2Provider Object
+$oAuth2:=New OAuth2 provider($params)
+
+// Create an Office365 object
+$Office365:=New Office365 provider($oAuth2)
+
+// Return a list with the first 100 users
+$informationList1:=$Office365.user.list()
+
+// Return a list of users whose displayName is Jean
+$userList2:=$Office365.user.list(New object("filter"; "startswith(displayName,'Jean')"))
+
+// return a list of users whose display names contain "F" and arrange it in descending order.
+$userList3:=$Office365.user.list(New object("search"; "\"displayName:F\""; "orderBy"; "displayName desc"; "select"; "displayName"))
+
+// Create a list filled with all the userPrincipalName
+
+$userList4:=$Office365.user.list(New object("select"; "userPrincipalName"))
+$col:=New collection
+Repeat
+ $col.combine($userList4.users)
+Until (Not($userList4.next()))
+```
+
+## See also
+
+[Google Class](./Google.md)
+[OAuth2Provider Class](./OAuth2Provider.md)
diff --git a/docs/Tutorial.md b/docs/Tutorial.md
new file mode 100644
index 0000000..a9fad02
--- /dev/null
+++ b/docs/Tutorial.md
@@ -0,0 +1,133 @@
+# Tutorials
+
+
+4D NetKit is a built-in 4D component that allows you to interact with third-party web services and their APIs, such as [Microsoft Graph](https://docs.microsoft.com/en-us/graph/overview).
+
+## Table of contents
+
+* [Authenticate to the Microsoft Graph API in service mode](#authenticate-to-the-microsoft-graph-api-in-service-mode)
+* (Archived) [Authenticate to the Microsoft Graph API in signedIn mode (4D NetKit), then send an email (SMTP Transporter class)](#authenticate-to-the-microsoft-graph-api-in-signedin-mode-and-send-an-email-with-smtp)
+
+
+**Warning:** Shared objects are not supported by the 4D NetKit API.
+
+## Authenticate to the Microsoft Graph API in service mode
+
+### Objectives
+
+Establish a connection to the Microsoft Graph API in service mode.
+
+### Prerequisites
+
+* You have registered an application with the [Microsoft identity platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) and obtained your application ID (also called client ID) and client secret.
+
+> Here, the term "application" does not refer to an application built in 4D. It refers to an entry point you create on the Azure portal. You use the generated client ID to tell your 4D application to trust the Microsoft identity platform.
+
+### Steps
+
+Once you have your client ID and client secret, you're ready to establish a connection to your Azure application.
+
+1. Open your 4D application, create a method and insert the following code:
+
+```4d
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $office365 : cs.NetKit.Office365
+
+var $credential:={}
+$credential.name:="Microsoft"
+$credential.permission:="service"
+
+$credential.clientId:="your-client-id" //Replace with your Microsoft identity platform client ID
+$credential.clientSecret:="your-client-secret" //Replace with your client secret
+$credential.tenant:="your-tenant-id" // Replace with your tenant ID
+$credential.scope:="https://graph.microsoft.com/.default"
+
+$oAuth2:=New OAuth2 provider($credential)
+
+$office365:=$cs.NetKit.Office365.new($oAuth2; {mailType: "MIME"})
+// In service mode, you need to indicate on behalf of which user you are sending the request:
+$office365.mail.userId:="MyUserPrincipalName"
+// Get mails of MyUserPrincipalName account
+$mailList:=$office365.mail.getMails()
+
+```
+
+2. Execute the method to establish the connection.
+
+## (Archived) Authenticate to the Microsoft Graph API in signedIn mode and send an email with SMTP
+
+> This tutorial has been archived. We recommend using the [Office365.mail.send()](./Office365.md#office365mailsend) method to send emails.
+
+#### Objectives
+
+Establish a connection to the Microsoft Graph API in signedIn mode, and send an email using the [SMTP Transporter class](http://developer.4d.com/docs/fr/API/SMTPTransporterClass.html).
+
+In this example, we get access [on behalf of a user](https://docs.microsoft.com/en-us/graph/auth-v2-user).
+
+#### Prerequisites
+
+* You have registered an application with the [Microsoft identity platform](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) and obtained your application ID (also called client ID).
+
+> Here, the term "application" does not refer to an application built in 4D. It refers to an entry point you create on the Azure portal. You use the generated client ID to tell your 4D application to trust the Microsoft identity platform.
+
+* You have a Microsoft e-mail account. For example, you signed up for an e-mail account with Microsoft's webmail services designated domains (@hotmail.com, @outlook.com, etc.).
+
+#### Steps
+
+Once you have your client ID, you're ready to establish a connection to your Azure application and send an email:
+
+1. Open your 4D application, create a method and insert the following code:
+
+```4d
+var $token; $param; $email : Object
+var $oAuth2 : cs.NetKit.OAuth2Provider
+var $address : Text
+
+// Configure authentication
+
+
+$param:=New object
+$param.name:="Microsoft"
+$param.permission:="signedIn"
+$param.clientId:="your-client-id" // Replace with your Microsoft identity platform client ID
+$param.redirectURI:="http://127.0.0.1:50993/authorize/"
+$param.scope:="https://outlook.office.com/SMTP.Send" // Get consent for sending an smtp email
+
+// Instantiate an object of the OAuth2Provider class
+$oAuth2:=New OAuth2 provider($param)
+
+// Request a token using the class function
+
+// Send a token request and start the a web server on the port specified in $param.redirectURI
+//to intercept the authorization response
+$token:=$oAuth2.getToken()
+
+// Set the email address for SMTP configuration
+$address:= "email-sender-address@outlook.fr" // Replace with your Microsoft email account address
+
+// Set the email's content and metadata
+$email:=New object
+$email.subject:="My first mail"
+$email.from:=$address
+$email.to:="email-recipient-address@outlook.fr" // Replace with the recipient's email address
+$email.textBody:="Test mail \r\n This is just a test e-mail \r\n Please ignore it"
+
+// Configure the SMTP connection
+$parameters:=New object
+$parameters.accessTokenOAuth2:=$token
+
+$parameters.authenticationMode:=SMTP authentication OAUTH2
+$parameters.host:="smtp.office365.com"
+$parameters.user:=$address
+
+// Send the email
+
+$smtp:=SMTP New transporter($parameters)
+$statusSend:=$smtp.send($email)
+
+```
+
+2. Execute the method. Your browser opens a page that allows you to authenticate.
+
+3. Log in to your Microsoft Outlook account and check that you've received the email.
+