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

# Managing API keys

> Provision and manage API key pairs over the API, including multi-account keys, your own public keys, and enabling or disabling keys.

API key pairs can be provisioned and managed over the API, in addition to the
[dashboard](/guides/dashboard/integrations/api-keys). This lets you automate
key provisioning at scale, such as issuing a key for each merchant during an
independent sales organization (ISO) onboarding flow.

## The Key Manager role

Managing API keys over the API requires the **Key Manager** role. This role is
scoped strictly to the API key lifecycle through the `api-key-pairs.read` and
`api-key-pairs.write` permissions. It grants no access to payments or back-office
data. The Administrator role can also manage keys, but Key Manager keeps key
management separate from payments and configuration access.

The Key Manager role is not granted by default and is assignable only to an API
key. Assign it from the [dashboard](/guides/dashboard/integrations/api-keys) to
the API key that provisions keys.

<Note>
  The **Administrator** and **Key Manager** roles can only be assigned by a
  dashboard user. An API key cannot create or update another key with either
  role, even when it holds the Key Manager role itself.
</Note>

## Create a key

Create a key pair with a `POST` to the `/api-key-pairs` endpoint. A key requires a
`display_name` and exactly one role in `role_ids`.

<CodeGroup>
  ```csharp C# theme={"system"}
  var apiKeyPair = await client.ApiKeyPairs.CreateAsync(
      new APIKeyPairCreate()
      {
          DisplayName = "Onboarding service",
          RoleIds = new List<string>() { "<role-id>" },
          MerchantAccountIds = new List<string>() { "default", "acme-au" },
      }
  );
  ```

  ```go Go theme={"system"}
  apiKeyPair, err := client.APIKeyPairs.Create(ctx, components.APIKeyPairCreate{
    DisplayName: "Onboarding service",
    RoleIds: []string{
      "<role-id>",
    },
    MerchantAccountIds: []string{
      "default",
      "acme-au",
    },
  })
  ```

  ```java Java theme={"system"}
  CreateApiKeyPairResponse response = gr4vyClient.apiKeyPairs().create()
    .request(APIKeyPairCreate.builder()
      .displayName("Onboarding service")
      .roleIds(List.of("<role-id>"))
      .merchantAccountIds(List.of("default", "acme-au"))
      .build())
    .call();
  ```

  ```php PHP theme={"system"}
  $apiKeyPairCreate = new APIKeyPairCreate(
    displayName: 'Onboarding service',
    roleIds: ['<role-id>'],
    merchantAccountIds: ['default', 'acme-au'],
  );
  $response = $client->apiKeyPairs->create(
    request: $apiKeyPairCreate
  );
  ```

  ```python Python theme={"system"}
  api_key_pair: models.APIKeyPair = client.api_key_pairs.create(
      display_name="Onboarding service",
      role_ids=["<role-id>"],
      merchant_account_ids=["default", "acme-au"],
  )
  ```

  ```ts TypeScript theme={"system"}
  const apiKeyPair = await gr4vy.apiKeyPairs.create({
    displayName: "Onboarding service",
    roleIds: ["<role-id>"],
    merchantAccountIds: ["default", "acme-au"],
  });
  ```
</CodeGroup>

By default, Gr4vy generates the key pair and returns the `private_key` once, in
the response to this request. Store it securely, as it cannot be retrieved again.

See the [Create API key](/reference/api-key-pairs/new-api-key-pair) reference for
more detail on this endpoint.

## Provide your own public key

Rather than have Gr4vy generate the key pair, you can supply your own public key
in the `public_key` field on create. When you do, Gr4vy stores the public key and
does not generate or return a private key, so the private key never leaves your
systems.

The public key must be a PEM-encoded ECDSA key on the P-521 (ES512) curve. RSA
keys are not accepted. Set `algorithm` to match, and note that the public key is
immutable for the life of the key.

<CodeGroup>
  ```csharp C# theme={"system"}
  var apiKeyPair = await client.ApiKeyPairs.CreateAsync(
      new APIKeyPairCreate()
      {
          DisplayName = "Onboarding service",
          RoleIds = new List<string>() { "<role-id>" },
          Algorithm = "ES512",
          PublicKey =
              "-----BEGIN PUBLIC KEY-----\n<pem-encoded-es512-public-key>\n-----END PUBLIC KEY-----",
      }
  );
  ```

  ```go Go theme={"system"}
  apiKeyPair, err := client.APIKeyPairs.Create(ctx, components.APIKeyPairCreate{
    DisplayName: "Onboarding service",
    RoleIds: []string{
      "<role-id>",
    },
    Algorithm: "ES512",
    PublicKey: gr4vy.String("-----BEGIN PUBLIC KEY-----\n<pem-encoded-es512-public-key>\n-----END PUBLIC KEY-----"),
  })
  ```

  ```java Java theme={"system"}
  CreateApiKeyPairResponse response = gr4vyClient.apiKeyPairs().create()
    .request(APIKeyPairCreate.builder()
      .displayName("Onboarding service")
      .roleIds(List.of("<role-id>"))
      .algorithm("ES512")
      .publicKey("-----BEGIN PUBLIC KEY-----\n<pem-encoded-es512-public-key>\n-----END PUBLIC KEY-----")
      .build())
    .call();
  ```

  ```php PHP theme={"system"}
  $apiKeyPairCreate = new APIKeyPairCreate(
    displayName: 'Onboarding service',
    roleIds: ['<role-id>'],
    algorithm: 'ES512',
    publicKey: "-----BEGIN PUBLIC KEY-----\n<pem-encoded-es512-public-key>\n-----END PUBLIC KEY-----",
  );
  $response = $client->apiKeyPairs->create(
    request: $apiKeyPairCreate
  );
  ```

  ```python Python theme={"system"}
  api_key_pair: models.APIKeyPair = client.api_key_pairs.create(
      display_name="Onboarding service",
      role_ids=["<role-id>"],
      algorithm="ES512",
      public_key="-----BEGIN PUBLIC KEY-----\n<pem-encoded-es512-public-key>\n-----END PUBLIC KEY-----",
  )
  ```

  ```ts TypeScript theme={"system"}
  const apiKeyPair = await gr4vy.apiKeyPairs.create({
    displayName: "Onboarding service",
    roleIds: ["<role-id>"],
    algorithm: "ES512",
    publicKey:
      "-----BEGIN PUBLIC KEY-----\n<pem-encoded-es512-public-key>\n-----END PUBLIC KEY-----",
  });
  ```
</CodeGroup>

## Issue a key for multiple merchant accounts

A single key can be scoped to more than one merchant account by passing an array
of merchant account IDs in `merchant_account_ids`. This is useful when one ISO or
brand groups several merchant accounts.

The list is editable after creation. Add or remove merchant accounts with a `PUT`
to the key, without regenerating the key or its private key.

To grant a key access to all merchant accounts, pass an empty list or omit
`merchant_account_ids`. This is only allowed when the caller can already access
all merchant accounts.

## Turn a key on or off

Each key has an `active` field that you can update over the API and in the
dashboard. Set `active` to `false` to turn off a key. A request authenticated
with an off key is rejected with a `401 Unauthorized` response, which lets you
revoke access immediately without deleting the key.

<CodeGroup>
  ```csharp C# theme={"system"}
  var apiKeyPair = await client.ApiKeyPairs.UpdateAsync(
      apiKeyPairId: "<api-key-pair-id>",
      apiKeyPairUpdate: new APIKeyPairUpdate()
      {
          Active = false,
      }
  );
  ```

  ```go Go theme={"system"}
  apiKeyPair, err := client.APIKeyPairs.Update(ctx, "<api-key-pair-id>", components.APIKeyPairUpdate{
    Active: gr4vy.Bool(false),
  })
  ```

  ```java Java theme={"system"}
  UpdateApiKeyPairResponse response = gr4vyClient.apiKeyPairs().update()
    .apiKeyPairId("<api-key-pair-id>")
    .apiKeyPairUpdate(APIKeyPairUpdate.builder()
      .active(false)
      .build())
    .call();
  ```

  ```php PHP theme={"system"}
  $apiKeyPairUpdate = new APIKeyPairUpdate(
    active: false,
  );
  $response = $client->apiKeyPairs->update(
    apiKeyPairId: '<api-key-pair-id>',
    apiKeyPairUpdate: $apiKeyPairUpdate
  );
  ```

  ```python Python theme={"system"}
  api_key_pair: models.APIKeyPair = client.api_key_pairs.update(
      api_key_pair_id="<api-key-pair-id>",
      active=False,
  )
  ```

  ```ts TypeScript theme={"system"}
  const apiKeyPair = await gr4vy.apiKeyPairs.update(
    {
      active: false,
    },
    "<api-key-pair-id>",
  );
  ```
</CodeGroup>

## Lifecycle webhooks

Gr4vy emits [webhook events](/guides/features/webhooks/events) as keys change, so
you can keep an audit trail of provisioning:

* `api-key-pair.created`
* `api-key-pair.updated`
* `api-key-pair.deleted`

The `api-key-pair.updated` event covers all changes to an existing key, including
enabling or disabling it and editing its merchant accounts. Subscribe to these
events through the existing [webhook subscription](/reference/webhook-subscriptions/new-webhook-subscription)
mechanism.
