diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 6960627c99..f162162d9f 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -99,11 +99,11 @@ get_all_tasks_paginating_2: |- -X GET 'http://localhost:7700/tasks?limit=2&from=8 get_one_key_1: |- curl \ - -X GET 'http://localhost:7700/keys/d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4' \ + -X GET 'http://localhost:7700/keys/6062abda-a5aa-4414-ac91-ecd7944c0f8d' \ -H 'Authorization: Bearer MASTER_KEY' get_all_keys_1: |- curl \ - -X GET 'http://localhost:7700/keys' \ + -X GET 'http://localhost:7700/keys?limit=3' \ -H 'Authorization: Bearer MASTER_KEY' create_a_key_1: |- curl \ @@ -118,24 +118,16 @@ create_a_key_1: |- }' update_a_key_1: |- curl \ - -X PATCH 'http://localhost:7700/keys/d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4' \ + -X PATCH 'http://localhost:7700/keys/6062abda-a5aa-4414-ac91-ecd7944c0f8d' \ -H 'Authorization: Bearer MASTER_KEY' \ -H 'Content-Type: application/json' \ --data-binary '{ - "description": "Manage documents: Products/Reviews API key", - "actions": [ - "documents.add", - "documents.delete" - ], - "indexes": [ - "products", - "reviews" - ], - "expiresAt": "2042-04-02T00:42:42Z" + "name": "Products/Reviews API key", + "description": "Manage documents: Products/Reviews API key" }' delete_a_key_1: |- curl \ - -X DELETE 'http://localhost:7700/keys/d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4' \ + -X DELETE 'http://localhost:7700/keys/6062abda-a5aa-4414-ac91-ecd7944c0f8d' \ -H 'Authorization: Bearer MASTER_KEY' get_settings_1: |- curl \ @@ -786,10 +778,10 @@ security_guide_search_key_1: |- -H 'Authorization: Bearer API_KEY' security_guide_update_key_1: |- curl \ - -X PATCH 'http://localhost:7700/keys/d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4' \ + -X PATCH 'http://localhost:7700/keys/74c9c733-3368-4738-bbe5-1d18a5fecb37 \ -H 'Authorization: Bearer MASTER_KEY' \ -H 'Content-Type: application/json' \ - --data-binary '{ "indexes": ["doctors"] }' + --data-binary '{ "description": "Default Search API Key" }' security_guide_create_key_1: |- curl \ -X POST 'http://localhost:7700/keys' \ @@ -807,7 +799,7 @@ security_guide_list_keys_1: |- -H 'Authorization: Bearer MASTER_KEY' security_guide_delete_key_1: |- curl \ - -X DELETE 'http://localhost:7700/keys/d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4' \ + -X DELETE 'http://localhost:7700/keys/ac5cd97d-5a4b-4226-a868-2d0eb6d197ab' \ -H 'Authorization: Bearer MASTER_KEY' primary_field_guide_create_index_primary_key: |- curl \ diff --git a/learn/advanced/updating.md b/learn/advanced/updating.md index f4ed1d31a5..7592b6b867 100644 --- a/learn/advanced/updating.md +++ b/learn/advanced/updating.md @@ -102,6 +102,10 @@ docker run -it --rm \ :::: +:::note +If you are updating to v0.28, keys imported from the old version will have their `key` and `uid` fields regenerated. +::: + ### Proceed according to your database version Now that you know which Meilisearch version your database is compatible with, proceed accordingly: diff --git a/learn/security/master_api_keys.md b/learn/security/master_api_keys.md index cff165cde6..45df62d8e9 100644 --- a/learn/security/master_api_keys.md +++ b/learn/security/master_api_keys.md @@ -82,27 +82,39 @@ Exposing your master key can give malicious users complete control over your Mei Meilisearch gives you fine-grained control over which users can access which indexes, endpoints, and routes. When protecting your instance with a master key, you can ensure only authorized users can carry out sensitive tasks such as adding documents or altering index settings. -The master key is the only key with access to the [`/keys` route](/reference/api/keys.md). This route allows you to [create](#creating-an-api-key), [update](#updating-an-api-key), [list](#listing-api-keys), and [delete](#deleting-an-api-key) API keys. +You can access the [`/keys` route](/reference/api/keys.md) using the master key or an API key with access to the `keys.get`, `keys.create`, `keys.update`, or `keys.delete` actions. This `/keys` route allows you to [create](#creating-an-api-key), [update](#updating-an-api-key), [list](#listing-api-keys), and [delete](#deleting-an-api-key) API keys. Though the default API keys are usually enough to manage the security needs of most applications, this might not be the case when dealing with privacy-sensitive data. In these situations, the fine-grained control offered by the `/keys` endpoint allows you to clearly decide who can access what information and for how long. +The [`key`](/reference/api/keys.md#key) field is generated by hashing the master key and the [`uid`](/reference/api/keys.md#uid). As a result, `key` values are deterministic between instances sharing the same configuration. Since the `key` field depends on the master key, it is not propagated to dumps and snapshots. If a malicious user ever gets access to your dumps or snapshots, they will not have access to your instance's API keys. + +It is, therefore, possible to determine the value of the `key` field by using the following command: + +```bash +echo -n $HYPHENATED_UUID | openssl dgst -sha256 -hmac $MASTER_KEY +``` + +This is also useful in continuous deployment processes as you know the value of the `key` field in advance. + ### Updating an API key -You can freely update an API key at any time, even after it expires. This includes editing the indexes, endpoints, and routes it can access, as well as its description and expiry date. +You can only update the `name` and `description` of an API key, even after it expires. -We can update the `Default Search API Key` so regular users cannot perform search operations in our `patient_medical_records` index: +For example, we can update the `Default Search API Key` and change its description: ```json { + "name": "Default Search API Key", "description": "Default Search API Key", "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", + "uid":"74c9c733-3368-4738-bbe5-1d18a5fecb37", "actions": [ "search" ], "indexes": [ - "doctors" + "*" ], "expiresAt": null, "createdAt": "2022-01-01T10:00:00Z", @@ -110,15 +122,15 @@ We can update the `Default Search API Key` so regular users cannot perform searc } ``` -To update an API key, you must use the [update API key endpoint](/reference/api/keys.md#update-a-key) which can only be accessed with the master key. +To update an API key, you must use the [update API key endpoint](/reference/api/keys.md#update-a-key), which can only be accessed with the master key or an API key with the `keys.update` action. -Meilisearch supports partial updates with the `PATCH` route. This means your payload only needs to contain the data you want to update—in this case, `indexes`. +Meilisearch supports partial updates with the `PATCH` route. This means your payload only needs to contain the data you want to update—in this case, `description`. ### Creating an API key -You can create API keys by using the [create key endpoint](/reference/api/keys.md#create-a-key). This endpoint is always protected and can only be accessed with the master key. +You can create API keys by using the [create key endpoint](/reference/api/keys.md#create-a-key). This endpoint is always protected and can only be accessed with the master key or an API key with the `keys.create` action. -Since we have altered the permissions in our default search key, we need to create a new API key so authorized users can search through out `patient_medical_records` index: +Let's create a new API key so authorized users can search through out `patient_medical_records` index: @@ -126,8 +138,10 @@ All [`/keys` endpoints](/reference/api/keys.md) are synchronous, so your key wil ```json { + "name": null, "description": "Search patient records key", "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", + "uid": "ac5cd97d-5a4b-4226-a868-2d0eb6d197ab", "actions": [ "search" ], @@ -146,9 +160,9 @@ It is good practice to always set an expiry date when creating a new API key. If You can use the [list keys endpoint](/reference/api/keys.md) to obtain information on any active key in your Meilisearch instance. This is useful when you need an overview of existing keys and their permissions. -[`GET /keys`](/reference/api/keys.md#get-all-keys) returns a full list of all existing keys. **Expired keys will appear in the response, but deleted keys will not**. As with creating, deleting, and updating API keys, you need the master key to access this endpoint. +By default, [`GET /keys`](/reference/api/keys.md#get-all-keys) returns the 20 most recently created keys. You can change this using the [`limit`](/reference/api/keys.md#get-all-keys) query parameter. **Expired keys will appear in the response, but deleted keys will not**. As with creating, deleting, and updating API keys, you either need the master key or an API key with the `keys.get` action to access this endpoint. -[`GET /keys/{key}`](/reference/api/keys.md#get-one-key) returns information on a single key. `{key}` should be replaced with the full `key` value obtained during key creation. +[`GET /keys/{key_or_uid}`](/reference/api/keys.md#get-one-key) returns information on a single key. `{key_or_uid}` should be replaced with the full `key` or `uid` value obtained during key creation. We can query our instance to confirm which active keys can search our `patient_medical_records` index: @@ -158,34 +172,40 @@ We can query our instance to confirm which active keys can search our `patient_m { "results": [ { - "description": "Default Search API Key (Use it to search from the frontend code)", - "key": "0a6e572506c52ab0bd6195921575d23092b7f0c284ab4ac86d12346c33057f99", + "name": "Default Search API Key", + "description": "Use it to search from the frontend", + "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", + "uid":"74c9c733-3368-4738-bbe5-1d18a5fecb37", "actions": [ - "search" + "search" ], "indexes": [ - "doctors" + "*" ], "expiresAt": null, - "createdAt": "2021-08-11T10:00:00Z", - "updatedAt": "2021-08-11T10:00:00Z" + "createdAt": "2022-01-01T10:00:00Z", + "updatedAt": "2022-01-01T10:00:00Z" }, { - "description": "Default Admin API Key (Use it for all other operations. Caution! Do not share it on the client side)", + "name": "Default Admin API Key", + "description": "Use it for all other than search operations. Caution! Do not expose it on a public frontend", "key": "380689dd379232519a54d15935750cc7625620a2ea2fc06907cb40ba5b421b6f", + "uid": "20f7e4c4-612c-4dd1-b783-7934cc038213", "actions": [ - "*" + "*" ], "indexes": [ - "*" + "*" ], "expiresAt": null, "createdAt": "2021-08-11T10:00:00Z", "updatedAt": "2021-08-11T10:00:00Z" }, { + "name": null, "description": "Search patient records key", "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", + "uid": "ac5cd97d-5a4b-4226-a868-2d0eb6d197ab", "actions": [ "search" ], @@ -196,7 +216,10 @@ We can query our instance to confirm which active keys can search our `patient_m "createdAt": "2022-01-01T10:00:00Z", "updatedAt": "2022-01-01T10:00:00Z" } - ] + ], + "offset":0, + "limit":20, + "total":3 } ``` @@ -212,8 +235,6 @@ If we accidentally exposed our `Search patient records key`, we can delete it to Once a key is past its `expiresAt` date, using it for API authorization will return an error. Expired keys will still be returned by the [list keys endpoint](/reference/api/keys.md#get-all-keys). -If you must continue using an expired key, you may use the [update key endpoint](/reference/api/keys.md#update-a-key) to set a new `expiresAt` date and effectively reactivate it. - ## Changing the master key To change the master key, first terminate your Meilisearch instance. Then relaunch it, [supplying a new value for the master key](#protecting-a-meilisearch-instance). diff --git a/learn/security/tenant_tokens.md b/learn/security/tenant_tokens.md index 21939b7087..aff1e6c2fd 100644 --- a/learn/security/tenant_tokens.md +++ b/learn/security/tenant_tokens.md @@ -58,6 +58,7 @@ Using a third-party library for tenant token generation is fairly similar to cre const jwt = require('jsonwebtoken'); const apiKey = 'my_api_key'; +const apiKeyUid = 'ac5cd97d-5a4b-4226-a868-2d0eb6d197ab'; const currentUserID = 'a_user_id'; const tokenPayload = { @@ -66,18 +67,18 @@ const tokenPayload = { 'filter': `user_id = ${currentUserID}` } }, - apiKeyPrefix: apiKey.substring(0, 8), + apiKeyUid: apiKeyUid, exp: parseInt(Date.now() / 1000) + 20 * 60 // 20 minutes }; const token = jwt.sign(tokenPayload, apiKey, {algorithm: 'HS256'}); ``` -`tokenPayload` contains the token payload. It must contain three fields: `searchRules`, `apiKeyPrefix`, and `exp`. +`tokenPayload` contains the token payload. It must contain three fields: `searchRules`, `apiKeyUid`, and `exp`. `searchRules` must be a JSON object containing a set of search rules. These rules specify restrictions applied to every query using this web token. -`apiKeyPrefix` must be the first 8 characters of a valid Meilisearch API key. +`apiKeyUid` must be the `uid` of a valid Meilisearch API key. `exp` is the only optional parameter of a tenant token. It must be a UNIX timestamp specifying the expiration date of the token. @@ -117,7 +118,7 @@ The token payload contains most of the relevant token data. It must be an object ```json { "exp": 1646756934, - "apiKeyPrefix": "12345678", + "apiKeyUid": "ac5cd97d-5a4b-4226-a868-2d0eb6d197ab", "searchRules": { "patient_medical_records": { "filter": "user_id = 1" @@ -188,7 +189,7 @@ The previous rules can be combined in one tenant token: ```json { - "apiKeyPrefix": "rkDxFUHd", + "apiKeyUid": "ac5cd97d-5a4b-4226-a868-2d0eb6d197ab", "exp": 1641835850, "searchRules": { "*": { diff --git a/reference/api/error_codes.md b/reference/api/error_codes.md index d9cd6ac93d..0d5404c834 100644 --- a/reference/api/error_codes.md +++ b/reference/api/error_codes.md @@ -165,6 +165,22 @@ The requested task does not exist. Please ensure that you are using the correct The `minWordSizeForTypos` object is invalid. The value for both `oneTypo` and `twoTypos` should be between `0` and `255`, and `twoTypos` should be greater or equal to `oneTypo`. +### `immutable_field` + +The field you are trying to modify is immutable. + +### `api_key_already_exists` + +A key with this `uid` already exists. + +### `invalid_api_key_uid` + +The given `uid` is invalid. The `uid` must follow the [uuid v4](https://www.sohamkamani.com/uuid-versions-explained) format. + +### `invalid_api_key_name` + +The given `name` is invalid. It should either be a string or `null`. + ### `invalid_task_status` The requested task status is invalid. Please use one of [these four possible values](/learn/advanced/asynchronous_operations.md#task-status). diff --git a/reference/api/keys.md b/reference/api/keys.md index 0f83bffc39..082a3b506b 100644 --- a/reference/api/keys.md +++ b/reference/api/keys.md @@ -1,12 +1,6 @@ # Keys -The `/keys` route allows you to create, manage, and delete API keys. To use these endpoints, you must [set the master key](/learn/configuration/instance_options.md#master-key) and supply it in the header of the request: - -```bash -curl \ - -H 'Authorization: Bearer MASTER_KEY' - … -``` +The `/keys` route allows you to create, manage, and delete API keys. To use these endpoints, you must first [set the master key](/learn/configuration/instance_options.md#master-key). Once a master key is set, you can access these endpoints by supplying it in the header of the request, or using API keys that have access to the `keys.get`, `keys.create`, `keys.update`, or `keys.delete` actions. [Learn more about managing keys and their rights](/learn/security/master_api_keys.md). @@ -14,10 +8,17 @@ curl \ -List all existing API keys. **Expired keys are included in the response**, but deleted keys are not. +List the 20 most recently created keys. **Expired keys are included in the response**, but deleted keys are not. Results can be paginated by using the `offset` and `limit` query parameters. [See below for an explanation of returned fields.](#description) +#### Query parameters + +| Query Parameter | Description | Default Value | +| ------------------------ | --------------------------| ------------- | +| **offset** | Number of keys to skip | 0 | +| **limit** | Number of keys to return | 20 | + ### Example @@ -28,8 +29,10 @@ List all existing API keys. **Expired keys are included in the response**, but d { "results": [ { + "name": null, "description": "Manage documents: Products/Reviews API key", "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", + "uid": "6062abda-a5aa-4414-ac91-ecd7944c0f8d", "actions": [ "documents.add", "documents.delete" @@ -43,8 +46,10 @@ List all existing API keys. **Expired keys are included in the response**, but d "updatedAt": "2021-10-13T15:00:00Z" }, { - "description": "Default Search API Key (Use it to search from the frontend code)", + "name": "Default Search API Key", + "description": "Use it to search from the frontend code", "key": "0a6e572506c52ab0bd6195921575d23092b7f0c284ab4ac86d12346c33057f99", + "uid": "74c9c733-3368-4738-bbe5-1d18a5fecb37", "actions": [ "search" ], @@ -56,8 +61,10 @@ List all existing API keys. **Expired keys are included in the response**, but d "updatedAt": "2021-08-11T10:00:00Z" }, { - "description": "Default Admin API Key (Use it for all other operations. Caution! Do not share it on the client side)", + "name": "Default Admin API Key", + "description": "Use it for anything that is not a search operation. Caution! Do not expose it on a public frontend", "key": "380689dd379232519a54d15935750cc7625620a2ea2fc06907cb40ba5b421b6f", + "uid": "20f7e4c4-612c-4dd1-b783-7934cc038213", "actions": [ "*" ], @@ -68,22 +75,61 @@ List all existing API keys. **Expired keys are included in the response**, but d "createdAt": "2021-08-11T10:00:00Z", "updatedAt": "2021-08-11T10:00:00Z" } - ] + ], + "offset":0, + "limit":3, + "total":7 } ``` +::: note +API keys are displayed in descending order based on their `createdAt` date. This means that the most recently created keys appear first. +::: + ### Returned fields +Returns API keys in an array called `results`, along with the following fields: + +#### `offset` + +The number of keys skipped over. + +#### `limit` + +The maximum number of keys to be returned by the request. + +#### `total` + +The total number of API keys that can be browsed. + +### The `results` array + +For each key, it returns: + +#### `name` + +A human-readable name for the key. Default value is `null`. + #### `description` A description for the key. Default value is `null`. +#### `uid` + +A [uuid v4](https://www.sohamkamani.com/uuid-versions-explained) to identify the API key. If not specified, it is automatically generated by Meilisearch. + #### `key` -An alphanumeric key value generated by Meilisearch on API key creation. Used for authorization when [making calls to a protected Meilisearch instance](/learn/security/master_api_keys.md#communicating-with-a-protected-instance). +An alphanumeric key value generated by Meilisearch by hashing the `uid` and the master key on API key creation. Used for authorization when [making calls to a protected Meilisearch instance](/learn/security/master_api_keys.md#communicating-with-a-protected-instance). This value is also used as the `{key}` path variable to [update](#update-a-key), [delete](#delete-a-key), or [get](#get-one-key) a specific key. +::: note +Since `key` is a hash of the `uid` and master key, `key` values are deterministic between instances sharing the same configuration. This means if the master key changes, all `key` values are automatically changed. + +Since the `key` field depends on the master key, it is computed at runtime and therefore not propagated to dumps and snapshots. As a result, even if a malicious user comes into possession of your dumps or snapshots, they will not have access to your instance's API keys. +::: + #### `actions` An array of API actions permitted for the key. `["*"]` for all actions. @@ -108,9 +154,9 @@ Date and time when the key was last updated, represented in RFC 3339 format. ## Get one key - + -Get information on the specified key. Attempting to use this endpoint with a non-existent or deleted key will result in [an error](/reference/api/error_codes.md#api-key-not-found). A valid API [key](/reference/api/keys.md#key) is required. +Get information on the specified key. Attempting to use this endpoint with a non-existent or deleted key will result in [an error](/reference/api/error_codes.md#api-key-not-found). A valid API [key](/reference/api/keys.md#key) or [uid](/reference/api/keys.md#uid) is required. ### Example @@ -120,8 +166,10 @@ Get information on the specified key. Attempting to use this endpoint with a non ```json { + "name": null, "description": "Add documents: Products API key", "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", + "uid": "6062abda-a5aa-4414-ac91-ecd7944c0f8d", "actions": [ "documents.add" ], @@ -146,6 +194,20 @@ Only the `indexes`, `actions`, and `expiresAt` fields are mandatory. ### Body +#### `name` + +**Type:** string +**Default value:** `null` + +A human-readable name for the key. + +#### `uid` + +**Type:** string +**Default value:** none + +A [uuid v4](https://www.sohamkamani.com/uuid-versions-explained) to identify the API key. If not specified, it is generated by Meilisearch. + #### `description` **Type:** string @@ -176,6 +238,10 @@ A list of API actions permitted for the key. `["*"]` for all actions. | stats.get | Provides access to the [get stats of an index](/reference/api/stats.md#get-stats-of-an-index) endpoint and the [get stats of all indexes](/reference/api/stats.md#get-stats-of-all-indexes) endpoint. For the latter, **non-authorized `indexes` are omitted from the response**. | | dumps.create | Provides access to the [create dump](/reference/api/dump.md#create-a-dump) endpoint. **Not restricted by `indexes`.** | | version | Provides access to the [get Meilisearch version](/reference/api/version.md#get-version-of-meilisearch) endpoint. | +| keys.get | Provides access to the [get all keys](#get-all-keys) endpoint.| +| keys.create | Provides access to the [create key](#create-a-key) endpoint.| +| keys.update | Provides access to the [update key](#update-a-key) endpoint.| +| keys.delete | Provides access to the [delete key](#delete-a-key) endpoint.| #### `indexes` @@ -201,25 +267,27 @@ Date and time when the key will expire, represented in RFC 3339 format. `null` i ```json { - "description": "Add documents: Products API key", - "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", - "actions": [ - "documents.add" - ], - "indexes": [ - "products" - ], - "expiresAt": "2021-11-13T00:00:00Z", - "createdAt": "2021-11-12T10:00:00Z", - "updatedAt": "2021-11-12T10:00:00Z" + "name": null, + "description": "Manage documents: Products/Reviews API key", + "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", + "uid": "6062abda-a5aa-4414-ac91-ecd7944c0f8d", + "actions": [ + "documents.add" + ], + "indexes": [ + "products" + ], + "expiresAt": "2021-11-13T00:00:00Z", + "createdAt": "2021-11-12T10:00:00Z", + "updatedAt": "2021-11-12T10:00:00Z" } ``` ## Update a key - + -Update the description, permissions, or expiration date of an API key. A valid API [key](/reference/api/keys.md#key) is required. +Update the name and description of an API key. A valid API [key](/reference/api/keys.md#key) or [uid](/reference/api/keys.md#uid) is required. To learn more about the variables sent in the body of the request, see the [create key](#body) endpoint. @@ -233,27 +301,29 @@ Updates to keys are **partial**. This means you should provide only the fields y ```json { - "description": "Manage documents: Products/Reviews API key", - "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", - "actions": [ - "documents.add", - "documents.delete" - ], - "indexes": [ - "products", - "reviews" - ], - "expiresAt": "2021-12-31T23:59:59Z", - "createdAt": "2021-11-12T10:00:00Z", - "updatedAt": "2021-10-12T15:00:00Z" + "name": "Products/Reviews API key", + "description": "Manage documents: Products/Reviews API key", + "key": "d0552b41536279a0ad88bd595327b96f01176a60c2243e906c52ac02375f9bc4", + "uid": "6062abda-a5aa-4414-ac91-ecd7944c0f8d", + "actions": [ + "documents.add", + "documents.delete" + ], + "indexes": [ + "products", + "reviews" + ], + "expiresAt": "2021-12-31T23:59:59Z", + "createdAt": "2021-10-12T00:00:00Z", + "updatedAt": "2021-10-13T15:00:00Z" } ``` ## Delete a key - + -Delete the specified API key. A valid API [key](/reference/api/keys.md#key) is required. +Delete the specified API key. A valid API [key](/reference/api/keys.md#key) or [uid](/reference/api/keys.md#uid) is required. ### Example