API Keys
API keys let your programs call the MITRITY REST API without a person signing in. CI/CD pipelines, automation scripts, custom integrations and SDKs use them to authenticate their requests.
You can bind a key to one environment, limit what it may do, and rotate it. Every key that is created, revoked or moved to another environment is recorded on the audit trail.
Overview
Every request to the MITRITY API must be authenticated. People who use the dashboard sign in with email and password, Google SSO or SAML, and their requests carry a session token. Programs use an API key instead, sent as a Bearer token:
curl https://api.mitrity.com/api/v1/agents \
-H "Authorization: Bearer mk_a1b2c3d4e5f6..."
Key Format
Every MITRITY API key has the same format:
mk_{64-character-hex}
| Component | Description | Example |
|---|---|---|
mk_ | Prefix identifying a MITRITY API key | mk_ |
{64-character-hex} | Random 64-character hexadecimal string | a1b2c3d4e5f6... |
The key string does not encode the environment or the access level. MITRITY stores both the environment scope and the access scopes as metadata on the key.
The first 8 characters (mk_ + 5 hex) are kept as the key's prefix. The prefix is not secret: it identifies the key in lists and audit logs.
A complete key looks like this: mk_a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef1234
Environment Scoping
An API key is either tenant-wide or scoped to one environment.
Tenant-Wide Keys
A tenant-wide key reaches resources in every environment its creator can reach. Each request names its environment in a query parameter or in the request body.
Only an Owner, or a user whose access scope is all, can create a tenant-wide key or make an existing key tenant-wide. Anyone else gets 403 Forbidden, and binds their keys to an environment they work in.
Use a tenant-wide key for a CI/CD pipeline that works across several environments.
Environment-Scoped Keys
An environment-scoped key works in its own environment and nowhere else, whoever created it. With a scoped key:
- Lists return only what belongs to that environment: its agents, its policies and the records about its agents. If the key's creator is scoped, the key sees only the part of that environment the creator can see.
- A single resource of another environment answers
404 Not Found, the same answer an unknown ID gets. - A create that names no environment is placed in the key's environment. A create that names another environment is refused with
403 Forbidden. This applies to agents, policies, reports, report schedules and red-team campaigns. - Actions that belong to the whole organization are refused with
403 Forbidden, as they are for a scoped user. These are organization-wide reports, the usage of governed actions, connecting or changing a SaaS platform, and creating, changing or deleting a credential. - The key cannot change which environment any key is bound to, its own included (
403 Forbidden). It creates only keys bound to its own environment. A request for a key in another environment, or for a tenant-wide key, is refused with403 Forbidden. - The key takes no action that only an Owner may take, even when an Owner created it (
403 Forbidden). These actions are listed below. - If the key's binding cannot be read, the key is refused rather than widened.
The Owner-only actions that an environment-scoped key cannot take are:
- make anyone an Owner, by invitation or by a role change;
- change an Owner's role, profile, MFA or access;
- remove a user;
- reset a Manager's MFA;
- change the organization's settings or SAML SSO;
- open billing or change the plan;
- delete an environment, its own included, or the organization;
- change the governance gate or the assessment templates;
- resolve a review assigned to Owners;
- approve an agent directly;
- publish, retire or delete an AI policy statement.
Apart from these actions, a key created by an Owner does what a Manager's key could do, in its environment.
Use environment-scoped keys to give each environment its own keys in a multi-environment setup.
Access Level (Least-Privilege)
Separately from its environment, you can restrict a key to a least-privilege access level with scopes. The API enforces scopes as it authenticates each request, on top of the role of the user who created the key. A key can only narrow what its creator could do, never widen it.
- Full access (the default, no scopes): the key may read and write, up to the role of the user who created it.
- Read-only (
scopes: ["*:read"]): the key may make everyGETrequest. Every request that changes something (POST/PUT/PATCH/DELETE) is denied with a403, whatever the creator's role. - Custom (per-resource scopes): the key gets read-only or read & write access to the resources you choose, and every other resource is denied. In the dashboard, pick Custom in the access-level selector and set each resource to None, Read or Read & write. Each selection becomes a
<resource>:<action>scope: Read becomes<resource>:read, and Read & write becomes<resource>:*. A live preview shows the exact scope strings, for exampleagents:*, policies:read.
Use a read-only key for dashboards, reporting integrations and anything else that should never change platform state. Use a custom key for a narrow integration that touches only a few resources.
Scope format
Scopes are an array of "<resource>:<action>" strings:
| Part | Values |
|---|---|
resource | An API path segment (e.g. policies, agents, credentials) or * for all resources |
action | read, write, or * |
A bare "*" is shorthand for full access, the same as no scopes. A key with no scopes is unrestricted: only its creator's role limits it.
Creating a read-only key
In the dashboard, pick Read-only in the access-level selector when you create the key. Through the API, pass scopes:
curl -X POST https://api.mitrity.com/api/v1/api-keys \
-H "Authorization: Bearer mk_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Reporting integration (read-only)",
"scopes": ["*:read"]
}'
A write attempted with a read-only key returns:
{ "error": "API key scope does not permit this operation", "code": 403 }
Creating API Keys
Via the Dashboard
- Go to Settings > API Keys.
- Click Create API Key.
- Fill in the key details:
- Name: a descriptive name, for example
CI/CD Pipeline - Staging. - Access level: Full access, Read-only, or Custom per-resource read/write scopes.
- Environment: All environments (tenant-wide) or one environment. A scoped user picks one of the environments they work in.
- Expiration: an optional expiration date. Setting one is recommended for security.
- Name: a descriptive name, for example
- Click Create.
- Copy the key now. It is shown only once: after you close the dialog, the key value cannot be retrieved.
Via the API
curl -X POST https://api.mitrity.com/api/v1/api-keys \
-H "Authorization: Bearer mk_your-existing-key" \
-H "Content-Type: application/json" \
-d '{
"name": "CI/CD Pipeline - Staging",
"environment_id": "env_staging",
"expires_at": "2026-06-01T00:00:00Z"
}'
Response (201 Created):
{
"id": "key_8k2m4n",
"tenant_id": "tnt_4f9a",
"created_by": "user_jsmith",
"name": "CI/CD Pipeline - Staging",
"key_prefix": "mk_a1b2c",
"scopes": [],
"expires_at": "2026-06-01T00:00:00Z",
"created_at": "2026-03-01T10:00:00Z",
"environment_id": "env_staging",
"raw_key": "mk_a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef1234"
}
Important: the full key (raw_key) is returned only in this creation response. MITRITY stores a hash of it. From then on, only the key_prefix (the first 8 characters) identifies the key.
Key Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Descriptive name for the key |
environment_id | string | No | Scope to a specific environment. Omit for tenant-wide. |
scopes | string[] | No | Least-privilege access scopes (e.g. ["*:read"] for read-only). Omit for unrestricted (role-only). |
expires_at | datetime | No | Expiration timestamp (ISO 8601). Omit for non-expiring keys. |
Key Lifecycle
States
A key's state follows from its timestamps:
| State | Description |
|---|---|
| Active | No revoked_at, and expires_at is unset or in the future. The key authenticates requests. |
| Expired | expires_at is in the past. Cannot be used. |
| Revoked | revoked_at is set. Cannot be used. |
Active Keys
An active key authenticates API requests. It stays active until it expires or is revoked.
Expired Keys
Once a key passes its expires_at timestamp, requests that use it receive 401 Unauthorized. An unknown or revoked key gets the same response, so the response does not reveal which keys exist:
{ "error": "invalid API key", "code": 401 }
Revoked Keys
Revoking a key invalidates it permanently. Revocation is immediate: any in-flight request that uses the key fails.
Listing API Keys
curl https://api.mitrity.com/api/v1/api-keys \
-H "Authorization: Bearer mk_your-api-key"
Response:
{
"data": [
{
"id": "key_8k2m4n",
"tenant_id": "tnt_4f9a",
"created_by": "user_jsmith",
"name": "CI/CD Pipeline - Staging",
"key_prefix": "mk_a1b2c",
"scopes": [],
"expires_at": "2026-06-01T00:00:00Z",
"last_used_at": "2026-03-01T09:45:00Z",
"created_at": "2026-03-01T10:00:00Z",
"environment_id": "env_staging"
},
{
"id": "key_9j3n5p",
"tenant_id": "tnt_4f9a",
"created_by": "user_jsmith",
"name": "Production Monitoring",
"key_prefix": "mk_b2c3d",
"scopes": ["*:read"],
"last_used_at": "2026-03-01T10:02:00Z",
"created_at": "2026-02-15T10:00:00Z"
}
],
"total": 2,
"limit": 50,
"offset": 0
}
Revoked keys stay in the list, with revoked_at set. A field without a value (expires_at, last_used_at, revoked_at, environment_id) is left out of the key.
Who Sees and Manages Which Keys
| Caller | Sees and manages |
|---|---|
| Owner or Manager | Every key of the organization |
| Member | Only the keys they created. Another person's key answers 404 Not Found, as an unknown key does. |
| Viewer | Lists keys, but cannot create, change or revoke one (403 Forbidden) |
| A request made with an environment-scoped key | Only the keys bound to the same environment, within what its creator sees |
total counts the same keys the list shows.
Pagination
| Parameter | Type | Description |
|---|---|---|
limit | integer | Results per page (default: 50, max: 200) |
offset | integer | Number of keys to skip (default: 0) |
Updating API Key Scope
To change the environment scope of an existing key:
curl -X PATCH https://api.mitrity.com/api/v1/api-keys/key_8k2m4n/environment \
-H "Authorization: Bearer mk_your-api-key" \
-H "Content-Type: application/json" \
-d '{
"environment_id": "env_production"
}'
Send "environment_id": null to make the key tenant-wide again. That takes full access: an Owner, or a user whose access scope is all (403 otherwise).
Who can change which key:
- A scoped user binds a key only to an environment they work in. Any other environment answers
404. - A revoked key cannot be changed (
400). - Viewers cannot change keys (
403). - A member changes only the keys they created.
- A request made with an environment-scoped key cannot change the environment of any key (
403).
Response: the updated key, in the same shape as the list returns it:
{
"id": "key_8k2m4n",
"tenant_id": "tnt_4f9a",
"created_by": "user_jsmith",
"name": "CI/CD Pipeline - Staging",
"key_prefix": "mk_a1b2c",
"scopes": [],
"expires_at": "2026-06-01T00:00:00Z",
"last_used_at": "2026-03-01T09:45:00Z",
"created_at": "2026-03-01T10:00:00Z",
"environment_id": "env_production"
}
Changing the scope changes the key's metadata, not the key string. The key keeps working with its new scope, and its prefix stays the same.
Revoking API Keys
Revoking a key invalidates it at once:
curl -X DELETE https://api.mitrity.com/api/v1/api-keys/key_8k2m4n \
-H "Authorization: Bearer mk_your-api-key"
Response: the key with revoked_at set:
{
"id": "key_8k2m4n",
"tenant_id": "tnt_4f9a",
"created_by": "user_jsmith",
"name": "CI/CD Pipeline - Staging",
"key_prefix": "mk_a1b2c",
"scopes": [],
"expires_at": "2026-06-01T00:00:00Z",
"last_used_at": "2026-03-01T09:45:00Z",
"revoked_at": "2026-03-01T12:00:00Z",
"created_at": "2026-03-01T10:00:00Z",
"environment_id": "env_production"
}
Revocation is permanent. A revoked key cannot be reactivated. Create a new key if you need one.
Owners and Managers can revoke any key. A member revokes only the keys they created. Viewers cannot revoke keys (403).
Key Rotation
Rotating keys regularly limits the damage a compromised key can do. MITRITY recommends rotating API keys every 90 days.
Rotation Process
- Create a new key with the same scope and permissions as the old key.
- Update your applications to use the new key.
- Check that the new key works by making a test API call.
- Revoke the old key once all applications have been updated.
Zero-Downtime Rotation
To rotate without downtime:
- Create the new key.
- Deploy the new key to your applications alongside the old key. Both keys are valid at the same time.
- Once all applications are using the new key, revoke the old key.
Keeping Track of Keys
MITRITY does not remind you to rotate keys. To keep rotation on schedule:
- Set
expires_atwhen you create a key, for example 90 days out. An expired key stops working on its own, so a forgotten key cannot live on. - Review
last_used_at, whichGET /api/v1/api-keysreturns for every key that has been used. It is updated on each request the key makes. - Revoke keys you no longer use. A key that has not been used in 30 days may be orphaned.
Dashboard: Settings > API Keys
The API Keys tab in the dashboard lists the keys you can see, with their name, prefix, access level, environment, creation date and status. A key past its expiration date shows as Expired, with how long ago it expired. Revoked keys are listed separately, with the date they were revoked.
From the tab you can also:
- create a key, with a name, access level, environment and an optional expiration date. The key is shown once;
- change the environment a key is bound to, from the list;
- revoke a key, after a confirmation step.
Audit Trail
Creating a key, revoking it and changing its environment are recorded on your organization's audit trail as governance changes:
| Event | Written when | Records |
|---|---|---|
api_key.created | A key is created | The key's ID, prefix and name, and its environment (none for a tenant-wide key) |
api_key.revoked | A key is revoked | The key's ID, prefix and name, and its environment |
api_key.environment_changed | A key is bound to an environment, or made tenant-wide, after it was created | The key's ID, prefix and name, and its old and new environment |
Each entry names the person who made the change, and the API key they made it with, if any. The key itself and its hash are never recorded. A refused request writes nothing.
Using a key writes no entry: each key's last_used_at shows when it was last used. A key that reaches its expiration date writes no entry either. From then on it is refused, and the API Keys tab shows it as Expired.
To find the entries in the Audit Log, pick the source Governance Changes. Each entry shows its event type, with the key's prefix beneath it. Through the API:
curl "https://api.mitrity.com/api/v1/audit?source=control_plane&event_type=api_key.created" \
-H "Authorization: Bearer mk_your-api-key"
Like every change to people and their access, these entries are shown to Owners and Managers only. Members and Viewers do not see them in the audit log. A key belongs to no agent, so the entries are also hidden from a user whose access scope is scoped, and from a request made with an environment-scoped key.
Security Considerations
Key Storage
- Never commit API keys to source control. Use
.envfiles or secret managers. - Never expose keys in client-side code, such as browser JavaScript.
- Keep keys in environment variables or a secret management service, such as GCP Secret Manager, AWS Secrets Manager or HashiCorp Vault.
Key Permissions
- An API key acts with the current role and access scope of the user who created it. A key made by a scoped user sees only the agents and environments granted to that user. A change to the user's role, access scope, agent grants or environment access applies to the key at once.
- When a user is removed from the tenant, all keys created by that user are deleted and stop working at once.
- A key cannot exceed the permissions of its creator's role.
Rate Limiting
All of your tenant's API keys share one rate limit of 100 requests a minute. Dashboard sessions are counted separately, per person, so people using the dashboard do not use up your integrations' budget.
Best Practices
Use Environment-Scoped Keys
Create separate keys for each environment. A staging CI/CD pipeline should not use a key scoped to production.
Set Expiration Dates
Always set an expiration date on an API key. A key that never expires and is then forgotten becomes a security liability.
Use Descriptive Names
Name keys by their purpose and environment: "CI/CD Pipeline - Staging" is better than "API Key 1."
Rotate Every 90 Days
Follow a 90-day rotation schedule. Give each key an expiration date that matches it, and replace the key before that date so automation keeps working.
Monitor Last Used Dates
Review each key's last_used_at regularly through the API. A key that has not been used in 30 days may be orphaned, and should be revoked.
Limit Key Count
Create only the keys you need. Each active key adds to your attack surface, so consolidate where you can.
Related Documentation
- API Overview: authentication, rate limits and the endpoint reference
- RBAC: role-based permissions (keys inherit user permissions)
- Environments: environment scoping for API keys
- SAML SSO: interactive authentication (complementary to API keys)
- Billing: plans and what they include