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

# Manage API keys

> Create, use, list, rotate, disable, and delete ION API keys for machine-to-machine integrations.

API keys authenticate machine-to-machine integrations. These include scripts, automations, ETL jobs, MES bridges, IoT devices, and other systems that call ION without a person signing in. You create, manage, and delete them through the API or the UI. For user-facing applications where ION enforces a specific user's permissions, use [OAuth 2.0](/api-reference/authentication/oauth) instead.

## How API keys behave

* Each API key is **specific to the environment** it was generated in. A key created in production will not work in sandbox.
* A key holds the **same permissions the creating user had at the moment it was generated**. Later changes to that user's permissions do not affect the key.
* Generating a new key does **not** invalidate previously generated keys. They keep working until disabled.
* Any automation using a key **acts on behalf of the user who created it**.
* A key is **automatically disabled when its user is deactivated**.

<Note>
  You need the `APIKeyObject` family of permissions to work with API keys. See
  the [permissions
  reference](/administration/users-and-permissions/overview#permissions-reference) for
  details.
</Note>

<Warning>
  For automations and integrations, back the key with a [service
  account](#service-accounts) rather than an individual's user.
</Warning>

## Create a key

An org admin provisions API keys. Each key is bound to a user identity in your organization. Audit logs then reflect which integration made each call.

```graphql theme={null}
mutation CreateAPIKey {
  createApiKey {
    apikey {
      clientId
      clientSecret
    }
  }
}
```

The `clientSecret` is shown **once**. Capture it immediately and store it in a secrets manager such as AWS Secrets Manager, HashiCorp Vault, 1Password, or your CI/CD's encrypted secrets store. ION cannot retrieve a secret after creation.

<video src="https://www.loom.com/share/e0d9e07c3c3843fd9a630abfdd8c7836?sid=578d2875-b287-45f9-8724-99c4a6d15c35" />

## Get an access token

A key's `clientId` and `clientSecret` exchange for a short-lived access token through the client-credentials grant. ION then accepts that token on the `Authorization` header. Most integrations use a small client library that handles the exchange and caching for you. See the [Build an API client](/api-reference/guides/python-quickstart) for a runnable example.

To exchange the credentials directly, POST a `client_credentials` grant to the auth server for your environment:

```bash theme={null}
curl -X POST \
  --data-urlencode "grant_type=client_credentials" \
  -d "client_id=CLIENT_ID" \
  -d "client_secret=CLIENT_SECRET" \
  https://<AUTH_SERVER>/realms/api-keys/protocol/openid-connect/token
```

The auth server and API endpoint differ per environment:

| App URI                                                   | Auth Server                            | API Endpoint                                  |
| --------------------------------------------------------- | -------------------------------------- | --------------------------------------------- |
| `staging.firstresonance.io` / `sandbox.firstresonance.io` | `staging-auth.buildwithion.com`        | `https://staging-api.buildwithion.com`        |
| `app.firstresonance.io`                                   | `auth.buildwithion.com`                | `https://api.buildwithion.com`                |
| `staging.ion-gov.com` / `sandbox.ion-gov.com`             | `auth-staging-gov.buildwithion.com`    | `https://api-staging-gov.buildwithion.com`    |
| `app.ion-gov.com`                                         | `auth-production-gov.buildwithion.com` | `https://api-production-gov.buildwithion.com` |
| `staging.ion-aus.com`                                     | `auth-staging-aus.buildwithion.com`    | `https://api-staging-aus.buildwithion.com`    |
| `app.ion-aus.com`                                         | `auth-production-aus.buildwithion.com` | `https://api-production-aus.buildwithion.com` |

Append `/graphql` to the API endpoint for query requests, for example `https://staging-api.buildwithion.com/graphql`. For more runnable scripts, see the [ion-examples repository](https://github.com/FirstResonance/ion-examples).

## List keys

List your organization's API keys:

```graphql theme={null}
query APIKeys {
  apiKeys {
    edges {
      node {
        clientId
        clientSecret
        id
        _etag
      }
    }
  }
}
```

## Enable, disable, or regenerate a secret

Update a key with this mutation:

```graphql theme={null}
mutation UpdateAPIKey($input: APIKeyInput!) {
  updateApiKey(input: $input) {
    apikey {
      clientId
      clientSecret
      enabled
    }
  }
}
```

Set the variables:

```json theme={null}
{
  "input": {
    "clientId": "<your-client-id>",
    "_etag": "<your-api-keys-_etag>",
    "enabled": true,
    "regenerateSecret": false
  }
}
```

Set `enabled` to `false` to disable a key. Set `regenerateSecret` to `true` to rotate its secret. Disabling takes effect immediately. The next request using that key returns `401 Unauthorized`.

## Rotate a key

Rotate an API key when:

* The key might have been exposed, such as being committed to a public repo, leaked in logs, or held by an employee who has left.
* Your org's security policy requires periodic rotation.
* An integration is being decommissioned.

Rotate with zero downtime:

1. Provision a new key alongside the existing one.
2. Deploy the new key to the integration and verify traffic is succeeding with it.
3. Delete the old key.

The old and new keys are both valid during the cutover window. Consumers don't see auth failures.

## Delete a key

Delete a key with this mutation:

```graphql theme={null}
mutation DeleteAPIKey($input: APIKeyInput!) {
  deleteApiKey(input: $input) {
    apikey {
      clientId
    }
  }
}
```

Set the variables:

```json theme={null}
{
  "input": {
    "clientId": "<your-client-id>",
    "_etag": "<your-api-keys-_etag>"
  }
}
```

Deletion is immediate. The next request using that key returns `401 Unauthorized`.

## Service accounts

Service accounts decouple API keys from individual users who might get deactivated. They also make clear that the actions in ION come from a shared service rather than from one person. A service account is an email your company creates and maintains outside of any employee's email. Multiple people can access it as needed.

## Related

* [Authentication](/api-reference/authentication/overview)
* [Authenticate with OAuth 2.0](/api-reference/authentication/oauth)
* [Build an API client](/api-reference/guides/python-quickstart)
