OAuth with Zoom
The Zoom API uses OAuth 2.0 to authenticate and authorize users to make requests. To set up access credentials and request scopes for your app, create an OAuth app on the Marketplace. Follow the Create an OAuth App guide for a full walkthrough.
OAuth2 endpoints are located at https://zoom.us/oauth/
.
All Zoom OAuth and API endpoints must be called from the server side of your application. If called from the client side, CORS errors will be thrown.
On This Page
- Getting an Access Token
- Using an Access Token
- Refreshing an Access Token
- Revoking an Access Token
- The /me Context
- Advanced Practices
- OAuth with Chatbots
Getting an Access Token
To get an access token, you'll set scopes for the level of access that your app needs from the user. Zoom presents information about these scopes and access requests to the user. The user will Authorize or Decline these access permissions for your app. See OAuth Scopes for details.
Once the user Authorizes the scopes, your app can request an access token. To retain this access level, your app can request refesh tokens.
Follow the steps below to request access.
Step 1: Request User Authorization
Direct the user to https://zoom.us/oauth/authorize
with the following query parameters:
Query Parameter | Description |
---|---|
response_type | Access response type being requested. The supported authorization workflow requires the value code. |
redirect_uri | URI to handle successful user authorization. Must match with Development or Production Redirect URI in your OAuth app settings. |
client_id | OAuth application's Development or Production Client ID. |
state | (Optional) An opaque value that you can use to maintain state between the request and callback. The authorization server includes this value when redirecting the user-agent back to the your app. |
code_challenge | (Optional, but required if using PKCE) A challenge derived from the code verifier sent in the authorization request to verify against the code_verifier later. |
code_challenge_method | (Optional) A method that was used to derive the code challenge. Defaults to "plain" if not present in the request. Code verifier transformation method is "S256" or "plain". |
An example without optional parameters:
https://zoom.us/oauth/authorize?response_type=code&client_id=7lstjK9NTyett_oeXtFiEQ&redirect_uri=https://yourapp.example.com
This URL is the same as the Add Button link on the Zoom App Marketplace.
If this is the first time that you are requesting authorization from a user, the user will be prompted by Zoom to authorize access for your app.
If authorized, the user will be redirected to the redirect_uri
with the authorization code in the code query parameter.
https://yourapp.com/?code=obBEe8ewaL_KdyNjniT4KPd8ffDWt9fGB
Set the scopes for your app in the Zoom App Marketplace. See Set Scopes for details.
Preserving User State in Redirect URLs
To provide users context in your app when they are adding the app, attach a state query parameter to the redirect_uri
parameter of the add link. When the user is returned to your app, the state parameter will be included along with the authorization code.
For example:
https://zoom.us/oauth/authorize?response_type=code&client_id=7lstjK9NTyett_oeXtFiEQ&redirect_uri=https://yourapp.com&state={userState}
This would redirect the user to:
https://yourapp.com?state={userState}&code=obBEe8ewaL_KdyNjniT4KPd8ffDWt9fGB
Using Proof Key for Code Exchange (PKCE)
Zoom supports Proof Key for Code Exchange (PKCE) when requesting user tokens. This offers better security by enabling clients to use a code challenge and code exchange as part of the initial user authorization request. See rfc7636 for more information.
To use this feature, send the code_challenge
field and optional code_challenge_method
field in the user Authorization request. Then send the code_verifier
field in the request access token step.
If Zoom verifies that the code_challenge
and the code_verifier
values match, the token endpoint continues processing. If they do not match, you will receive an invalid_grant
error.
Step 2: Request Access Token
Make a POST request to https://zoom.us/oauth/token
with the following Request Headers and Request Body information:
HTTP Request Headers
Header | Description |
---|---|
Authorization | The string Basic with your Client ID and Client Secret separated with colon (: ), Base64-encoded. For example, Client_ID:Client_Secret Base64-encoded is Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ= . |
Content-Type | application/x-www-form-urlencoded |
Request Body
Request parameter | Description |
---|---|
code | The authorization code supplied to the callback by Zoom. |
grant_type | authorization_code |
redirect_uri | Your application's redirect URI. |
code_verifier | Optional, but required if using PKCE. A cryptographically random string used to correlate the authorization request to the token request. |
Here is an example of a request for the access token:
POST /oauth/token HTTP/1.1Host: zoom.usAuthorization: Basic Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ=Content-Type: application/x-www-form-urlencodedcode=[CODE]&grant_type=authorization_code&redirect_uri=[REDIRECT URI]&code_verifier=[CODE VERIFIER]
If successful, the Response Body will be a JSON response containing the user's access token:
{"access_token": "eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJ2ZXIiOiI2IiwiY2xpZW50SWQiOiI8Q2xpZW50X0lEPiIsImNvZGUiOiI8Q29kZT4iLCJpc3MiOiJ1cm46em9vbTpjb25uZWN0OmNsaWVudGlkOjxDbGllbnRfSUQ-IiwiYXV0aGVudGljYXRpb25JZCI6IjxBdXRoZW50aWNhdGlvbl9JRD4iLCJ1c2VySWQiOiI8VXNlcl9JRD4iLCJncm91cE51bWJlciI6MCwiYXVkIjoiaHR0cHM6Ly9vYXV0aC56b29tLnVzIiwiYWNjb3VudElkIjoiPEFjY291bnRfSUQ-IiwibmJmIjoxNTgwMTQ2OTkzLCJleHAiOjE1ODAxNTA1OTMsInRva2VuVHlwZSI6ImFjY2Vzc190b2tlbiIsImlhdCI6MTU4MDE0Njk5MywianRpIjoiPEpUST4iLCJ0b2xlcmFuY2VJZCI6MjV9.F9o_w7_lde4Jlmk_yspIlDc-6QGmVrCbe_6El-xrZehnMx7qyoZPUzyuNAKUKcHfbdZa6Q4QBSvpd6eIFXvjHw","token_type": "bearer","refresh_token": "eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJ2ZXIiOiI2IiwiY2xpZW50SWQiOiI8Q2xpZW50X0lEPiIsImNvZGUiOiI8Q29kZT4iLCJpc3MiOiJ1cm46em9vbTpjb25uZWN0OmNsaWVudGlkOjxDbGllbnRfSUQ-IiwiYXV0aGVudGljYXRpb25JZCI6IjxBdXRoZW50aWNhdGlvbl9JRD4iLCJ1c2VySWQiOiI8VXNlcl9JRD4iLCJncm91cE51bWJlciI6MCwiYXVkIjoiaHR0cHM6Ly9vYXV0aC56b29tLnVzIiwiYWNjb3VudElkIjoiPEFjY291bnRfSUQ-IiwibmJmIjoxNTgwMTQ2OTkzLCJleHAiOjIwNTMxODY5OTMsInRva2VuVHlwZSI6InJlZnJlc2hfdG9rZW4iLCJpYXQiOjE1ODAxNDY5OTMsImp0aSI6IjxKVEk-IiwidG9sZXJhbmNlSWQiOjI1fQ.Xcn_1i_tE6n-wy6_-3JZArIEbiP4AS3paSD0hzb0OZwvYSf-iebQBr0Nucupe57HUDB5NfR9VuyvQ3b74qZAfA","expires_in": 3599,"scope": "user:read:admin"}
You can use this access token to make requests to the Zoom API. Access tokens expire after one hour.
Using an Access Token
Make requests to the Zoom API by sending the access_token
as the Authorization Bearer header.
"Authorization": "Bearer <ACCESS_TOKEN>"
Example Request
GET
https://api.zoom.us/v2/users/me
Note: This endpoint uses the /me context.
Request Headers
{"Authorization": "Bearer eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJ2ZXIiOiI2IiwiY2xpZW50SWQiOiI8Q2xpZW50X0lEPiIsImNvZGUiOiI8Q29kZT4iLCJpc3MiOiJ1cm46em9vbTpjb25uZWN0OmNsaWVudGlkOjxDbGllbnRfSUQ-IiwiYXV0aGVudGljYXRpb25JZCI6IjxBdXRoZW50aWNhdGlvbl9JRD4iLCJ1c2VySWQiOiI8VXNlcl9JRD4iLCJncm91cE51bWJlciI6MCwiYXVkIjoiaHR0cHM6Ly9vYXV0aC56b29tLnVzIiwiYWNjb3VudElkIjoiPEFjY291bnRfSUQ-IiwibmJmIjoxNTgwMTQ2OTkzLCJleHAiOjE1ODAxNTA1OTMsInRva2VuVHlwZSI6ImFjY2Vzc190b2tlbiIsImlhdCI6MTU4MDE0Njk5MywianRpIjoiPEpUST4iLCJ0b2xlcmFuY2VJZCI6MjV9.F9o_w7_lde4Jlmk_yspIlDc-6QGmVrCbe_6El-xrZehnMx7qyoZPUzyuNAKUKcHfbdZa6Q4QBSvpd6eIFXvjHw"}
If successful, the response body will be a JSON representation of your user:
{"id": "KdYKjnimT4KPd8FFgQt9FQ","first_name": "Jane","last_name": "Dev","email": "jane.dev@email.com","type": 2,"role_name": "Owner","pmi": 1234567890,"use_pmi": false,"vanity_url": "https://janedevinc.zoom.us/my/janedev","personal_meeting_url": "https://janedevinc.zoom.us/j/1234567890","timezone": "America/Denver","verified": 1,"dept": "","created_at": "2019-04-05T15:24:32Z","last_login_time": "2019-12-16T18:02:48Z","last_client_version": "4.6.12611.1124(mac)","pic_url": "https://janedev.zoom.us/p/KdYKjnimFR5Td8KKdQt9FQ/19f6430f-ca72-4154-8998-ede6be4542c7-837","host_key": "533895","jid": "kdykjnimt4kpd8kkdqt9fq@xmpp.zoom.us","group_ids": [],"im_group_ids": ["3NXCD9VFTCOUH8LD-QciGw"],"account_id": "gVcjZnYYRLDbb_MfgHuaxg","language": "en-US","phone_country": "US","phone_number": "+1 1234567891","status": "active"}
Refreshing an Access Token
Access tokens expire after one hour. Once expired, you will have to refresh a user's access token.
Make a POST request to https://zoom.us/oauth/token
with the following HTTP request headers and request body:
Authorization Header | Description |
---|---|
Authorization | The string "Basic" with your Client ID and Client Secret with a colon (: ) in between, Base64-encoded. For example, Client_ID:Client_Secret Base64-encoded is Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ= . |
Content-Type | application/x-www-form-urlencoded |
Request Parameter | Description |
---|---|
grant_type | refresh_token |
refresh_token | Your refresh token. |
Request
Here's an example request for a refresh token:
POST /oauth/token HTTP/1.1Host: zoom.usAuthorization: Basic base64Encode(client_id:client_secret)Content-Type: application/x-www-form-urlencodedrefresh_token=[REFRESH TOKEN]&grant_type=refresh_token
Response
If successful, the response body will be a JSON representation of your user's refreshed access token:
{"access_token": "eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJ2ZXIiOiI2IiwiY2xpZW50SWQiOiI8Q2xpZW50X0lEPiIsImNvZGUiOiI8Q29kZT4iLCJpc3MiOiJ1cm46em9vbTpjb25uZWN0OmNsaWVudGlkOjxDbGllbnRfSUQ-IiwiYXV0aGVudGljYXRpb25JZCI6IjxBdXRoZW50aWNhdGlvbl9JRD4iLCJ1c2VySWQiOiI8VXNlcl9JRD4iLCJncm91cE51bWJlciI6MCwiYXVkIjoiaHR0cHM6Ly9vYXV0aC56b29tLnVzIiwiYWNjb3VudElkIjoiPEFjY291bnRfSUQ-IiwibmJmIjoxNTgwMTQ3Mzk0LCJleHAiOjE1ODAxNTA5OTQsInRva2VuVHlwZSI6ImFjY2Vzc190b2tlbiIsImlhdCI6MTU4MDE0NzM5NCwianRpIjoiPEpUST4iLCJ0b2xlcmFuY2VJZCI6MjZ9.5c58p0PflZJdlz4Y7PgMIVCrQpHDnbM565iCKlrtajZ5HHmy00P5FCcoMwHb9LxjsUgbJ7653EfdeX5NEm6RoA","token_type": "bearer","refresh_token": "eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJ2ZXIiOiI2IiwiY2xpZW50SWQiOiI8Q2xpZW50X0lEPiIsImNvZGUiOiI8Q29kZT4iLCJpc3MiOiJ1cm46em9vbTpjb25uZWN0OmNsaWVudGlkOjxDbGllbnRfSUQ-IiwiYXV0aGVudGljYXRpb25JZCI6IjxBdXRoZW50aWNhdGlvbl9JRD4iLCJ1c2VySWQiOiI8VXNlcl9JRD4iLCJncm91cE51bWJlciI6MCwiYXVkIjoiaHR0cHM6Ly9vYXV0aC56b29tLnVzIiwiYWNjb3VudElkIjoiPEFjY291bnRfSUQ-IiwibmJmIjoxNTgwMTQ3Mzk0LCJleHAiOjIwNTMxODczOTQsInRva2VuVHlwZSI6InJlZnJlc2hfdG9rZW4iLCJpYXQiOjE1ODAxNDczOTQsImp0aSI6IjxKVEk-IiwidG9sZXJhbmNlSWQiOjI2fQ.DwuqOzywRrQO2a6yp0K_6V-hR_i_mOB62flkr0_NfFdYsSqahIRRGk1GlUTQnFzHd896XDKf_FnSSvoJg_tzuQ","expires_in": 3599,"scope": "user:read"}
Refresh tokens expire after 15 years. The latest refresh token must always be used for the next refresh request.
Revoking an Access Token
To revoke a users access token, make a POST request to https://zoom.us/oauth/revoke
with the following request body parameter and authorization headers:
Parameter | Description |
---|---|
token | The current access token to revoke that is valid and not expired. |
Authorization Header | Description |
---|---|
Authorization | The string "Basic" with your Client ID and Client Secret with a colon (: ) in between, Base64-encoded. For example, Client_ID:Client_Secret Base64-encoded is Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ= . |
Content-Type | application/x-www-form-urlencoded |
Request
Here's an example request to revoke an access token:
POST /oauth/revoke HTTP/1.1Host: zoom.usAuthorization: Basic base64Encode(client_id:client_secret)Content-Type: application/x-www-form-urlencoded; charset=UTF-8 4token=[ACCESS TOKEN]
Response
If successful, the response body will be a JSON success object:
{"status": "success"}
Advanced Practices
Preserving User State in Redirect URLs
When directing a user to authorize your app, attach a state query parameter to the add link. When the user is returned to your app, the state parameter will be included along with the authorization code.
Example:
https://zoom.us/oauth/authorize?response_type=code&client_id=7lstjK9NTyett_oeXtFiEQ&state=<userstate>&redirect_uri=https://yourapp.com
This would redirect the user to:
https://yourapp.com/?code=obBEe8ewaL_KdyNjniT4KPd8ffDWt9fGB&state=<userstate>
Using Multiple Environments
To support multiple subdomain environments for your users, add any
as the subdomain of your Redirect URL (development & production). Example: https://any.yourapp.com
.
In your add link, dynamically set the respective subdomain on the redirect_uri
param:
For https://sub1.yourapp.com
:
https://zoom.us/oauth/authorize?response_type=code&client_id=7lstjK9NTyett_oeXtFiEQ&redirect_uri=https://sub1.yourapp.com
For https://sub2.yourapp.com
:
https://zoom.us/oauth/authorize?response_type=code&client_id=7lstjK9NTyett_oeXtFiEQ&redirect_uri=https://sub2.yourapp.com
The /me Context
For APIs where OAuth is available, you can replace the {userID}
path parameter with me
to restrict the context of the call to the user the token belongs to. This is a quick way to use user-specific endpoints instead of needing to lookup or store the user ID for each token.
Below is a list of requests which support the /me
context:
URL | Methods |
---|---|
/v2/users/me | GET, PATCH |
/v2/users/me/token | GET, DELETE |
/v2/users/me/settings | GET, PATCH |
/v2/users/me/meetings | GET, POST |
/v2/users/me/webinars | GET, POST |
/v2/users/me/recordings | GET |
/v2/users/me/assistants | GET, POST, DELETE |
/v2/users/me/assistants/{assistantId} | DELETE |
OAuth with Chatbots
Account-level OAuth apps can also be Chatbot apps, just as Chatbot apps can be account-level OAuth apps; however, OAuth apps and Chatbots have a different authentication flows.
Combining OAuth and Chatbot functionality into one app will require you to implement two authorization flows for both OAuth and Chatbot tokens.
Requesting an OAuth and Chatbot Token
Direct the user to https://zoom.us/oauth/authorize
with the following query parameters:
Query Parameter | Description |
---|---|
response_type | Access response type being requested. The supported authorization workflow requires the value code . |
redirect_uri | URI to handle successful user authorization. Must match with Development or Production Redirect URI in your OAuth app settings. |
client_id | OAuth application's Development or Production Client ID. |
Example
https://zoom.us/oauth/authorize?response_type=code&client_id=U1RqQ9UsQo6hd6fJQWFLQ&redirect_uri=https://yourapp.com
This URL is the same as the Add Button link on the Zoom App Marketplace.
If this is the first time that you are requesting authorization from a user, the user will be prompted by Zoom to authorize access for your app.
If authorized, the user will be redirected to the redirect_uri with the authorization code in the code query parameter.
https://yourapp.com/?code=obBEe8ewaL_KdyNjniT4KPd8ffDWt9fGB
Notice the code returned in the redirect URL. Follow Step 2 in Requesting an Access Token to obtain an OAuth Access Token.
To get a Chatbot token, make a POST request to https://zoom.us/oauth/token
with the following query parameters and authorization header:
Query Parameter | Description |
---|---|
grant_type | Value client_credentials . |
Authorization Header | Description |
---|---|
Authorization | The string "Basic" with your Client ID and Client Secret with a colon (: ) in between, Base64-encoded. For example, Client_ID:Client_Secret Base64-encoded is Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ= . |
Example Request
POST
https://zoom.us/oauth/token?grant_type=client_credentials
Request Headers
{"Authorization": "Basic Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ="}
If successful, the response body will be a JSON representation of the Chatbot token:
{"access_token": "eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ2ZXIiOiI2IiwibmJmIjoxNTgwMTQ2NjkzLCJjbGllbnRJZCI6IjxDbGllbnRfSUQ-IiwiaXNzIjoidXJuOnpvb206Y29ubmVjdDpjbGllbnRpZDo8Q2xpZW50X0lEPiIsImF1dGhlbnRpY2F0aW9uSWQiOiI8QXV0aGVudGljYXRpb25fSUQ-IiwiZXhwIjoxNTgwMTUwMjkzLCJ0b2tlblR5cGUiOiJjbGllbnRfdG9rZW4iLCJpYXQiOjE1ODAxNDY2OTMsInVzZXJJZCI6IjxVc2VyX0lEPiIsImdyb3VwTnVtYmVyIjowLCJqdGkiOiI8SlRJPiJ9.chgzEaGxeYl3alAaLsFSRvQFVRqoabM5BINVELOYPO1FqveoEk02i8AIGrtg0FiX779pMWpMObkxFnPQy7euNA","token_type": "bearer","expires_in": 3599,"scope": "imchat:bot"}
Chatbot tokens expire after one hour.
Using a Chatbot Token
Make requests to the Chatbot API by sending the Chatbot token as the Authorization Bearer header:
"Authorization": "Bearer <CHATBOT_TOKEN>"
Example Request
POST
https://api.zoom.us/v2/im/chat/messages
Request Headers
{"Authorization": "Bearer eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ2ZXIiOiI2IiwibmJmIjoxNTgwMTQ2NjkzLCJjbGllbnRJZCI6IjxDbGllbnRfSUQ-IiwiaXNzIjoidXJuOnpvb206Y29ubmVjdDpjbGllbnRpZDo8Q2xpZW50X0lEPiIsImF1dGhlbnRpY2F0aW9uSWQiOiI8QXV0aGVudGljYXRpb25fSUQ-IiwiZXhwIjoxNTgwMTUwMjkzLCJ0b2tlblR5cGUiOiJjbGllbnRfdG9rZW4iLCJpYXQiOjE1ODAxNDY2OTMsInVzZXJJZCI6IjxVc2VyX0lEPiIsImdyb3VwTnVtYmVyIjowLCJqdGkiOiI8SlRJPiJ9.chgzEaGxeYl3alAaLsFSRvQFVRqoabM5BINVELOYPO1FqveoEk02i8AIGrtg0FiX779pMWpMObkxFnPQy7euNA"}
Request Body
{"robot_jid": "v1zx6cy00psoylshypvg-iuq@xmpp.zoom.us","to_jid": "kdykjnimt4kpd8kkdqt9fq@xmpp.zoom.us","account_id": "gVcjZnWWRLWvv_GtyGuaxg","content": {"head": {"text": "Hello World"}}}
If successful, the response body will be a JSON representation of the sent Chatbot message:
{"message_id": "20191218175454248_UvRlxOz_aw1","robot_jid": "v1zx6cy00psoylshypvg-iuq@xmpp.zoom.us","sent_time": "2019-12-18 17:54:54","to_jid": "kdykjnimt4kpd8kkdqt9fq@xmpp.zoom.us"}
Refreshing a Chatbot Token
Refreshing a Chatbot token is the same process as requesting a Chatbot token. Make a POST request to https://zoom.us/oauth/token
with the following query parameters and authorization header:
Query Parameter | Description |
---|---|
grant_type | Value client_credentials . |
Authorization Header | Description |
---|---|
Authorization | The string "Basic" with your Client ID and Client Secret with a colon (: ) in between, Base64-encoded. For example, Client_ID:Client_Secret Base64-encoded is Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ= . |
Example Request
POST
https://zoom.us/oauth/token?grant_type=client_credentials
Request Headers
{"Authorization": "Basic Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ="}
If successful, the response body will be a JSON representation of your refreshed Chatbot token:
{"access_token": "eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ2ZXIiOiI2IiwibmJmIjoxNTgwMTQ2NjkzLCJjbGllbnRJZCI6IjxDbGllbnRfSUQ-IiwiaXNzIjoidXJuOnpvb206Y29ubmVjdDpjbGllbnRpZDo8Q2xpZW50X0lEPiIsImF1dGhlbnRpY2F0aW9uSWQiOiI8QXV0aGVudGljYXRpb25fSUQ-IiwiZXhwIjoxNTgwMTUwMjkzLCJ0b2tlblR5cGUiOiJjbGllbnRfdG9rZW4iLCJpYXQiOjE1ODAxNDY2OTMsInVzZXJJZCI6IjxVc2VyX0lEPiIsImdyb3VwTnVtYmVyIjowLCJqdGkiOiI8SlRJPiJ9.chgzEaGxeYl3alAaLsFSRvQFVRqoabM5BINVELOYPO1FqveoEk02i8AIGrtg0FiX779pMWpMObkxFnPQy7euNA","token_type": "bearer","expires_in": 3599,"scope": "imchat:bot"}
Chatbot tokens expire after one hour.
Need help?
If you're looking for help, try Developer Support or our Developer Forum. Priority support is also available with Premier Developer Support plans.