Operating

Manage API Keys

List, revoke, and delegate API keys to keep your Blnk deployment secure over time.

After you create a scoped key, you’ll audit what’s active, revoke keys you no longer need, and rotate keys before they expire.

If you haven’t created a key yet, start with Scoped API keys.

Listing, revoking, and delegating keys requires the master key or a scoped key with the matching api-keys:* scopes. See Scopes for permissions and Owner context for which keys a caller can manage.

List keys

List keys for an owner to see what’s active. The plaintext key value is never returned. You get metadata such as name, scopes, expiry, and last-used timestamp.

curl -X GET "http://localhost:5001/api-keys?owner=payments-team" \
  -H "X-blnk-key: <api-key>"
const response = await blnk.ApiKeys.list({
  owner: 'payments-team',
});
keys, _, _ := client.ApiKeys.List(&blnkgo.ListApiKeysOptions{
    Owner: "payments-team",
})
response = blnk.api_keys.list({
  "owner": "payments-team",
})
ApiResponse<JsonNode> response = blnk.apiKeys().list(
    ListApiKeysOptions.create()
        .owner("payments-team"));
[
  {
    "api_key_id": "api_key_879f0ecb-e29f-4137-801b-1048366381db",
    "name": "Payments Service",
    "owner_id": "payments-team",
    "scopes": ["transactions:write", "balances:read"],
    "expires_at": "2027-06-13T00:00:00Z",
    "created_at": "2026-06-13T10:30:00Z",
    "last_used_at": "2026-06-13T14:22:00Z"
  }
]

Revoke a key

Revoke a key when it’s no longer needed or you suspect it was exposed.

curl -X DELETE "http://localhost:5001/api-keys/api_key_879f0ecb-e29f-4137-801b-1048366381db?owner=payments-team" \
  -H "X-blnk-key: <api-key>"
const response = await blnk.ApiKeys.delete(
  'api_key_879f0ecb-e29f-4137-801b-1048366381db',
  {
    owner: 'payments-team',
  },
);
resp, _ := client.ApiKeys.Delete(
    "api_key_879f0ecb-e29f-4137-801b-1048366381db",
    &blnkgo.DeleteApiKeysOptions{
        Owner: "payments-team",
    },
)
response = blnk.api_keys.delete(
  "api_key_879f0ecb-e29f-4137-801b-1048366381db",
  {
    "owner": "payments-team",
  },
)
ApiResponse<JsonNode> response = blnk.apiKeys().delete(
    "api_key_879f0ecb-e29f-4137-801b-1048366381db",
    DeleteApiKeyOptions.create()
        .owner("payments-team"));

A successful revoke returns 204 No Content with an empty body. The key stops working on the next request.

Delegate key creation

A scoped key with api-keys:write can create narrower keys for its own owner, as long as it only grants scopes it already holds.

See Owner context for inheritance and cross-owner rules.

curl -X POST "http://localhost:5001/api-keys" \
  -H "X-blnk-key: <team-admin-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Nightly Reconciliation Job",
    "owner": "payments-team",
    "scopes": ["reconciliation:read"],
    "expires_at": "2027-01-01T00:00:00Z"
  }'
const response = await blnk.ApiKeys.create({
  name: 'Nightly Reconciliation Job',
  owner: 'payments-team',
  scopes: ['reconciliation:read'],
  expires_at: '2027-01-01T00:00:00Z',
});
expiresAt, _ := time.Parse(time.RFC3339, "2027-01-01T00:00:00Z")
apiKey, _, _ := client.ApiKeys.Create(blnkgo.CreateApiKeyRequest{
    Name: "Nightly Reconciliation Job",
    Owner: "payments-team",
    Scopes: []string{"reconciliation:read"},
    ExpiresAt: expiresAt,
})
response = blnk.api_keys.create({
  "name": "Nightly Reconciliation Job",
  "owner": "payments-team",
  "scopes": ["reconciliation:read"],
  "expires_at": "2027-01-01T00:00:00Z",
})
ApiResponse<JsonNode> response = blnk.apiKeys().create(
    CreateApiKeyData.create()
        .name("Nightly Reconciliation Job")
        .owner("payments-team")
        .scopes(List.of("reconciliation:read"))
        .expiresAt("2027-01-01T00:00:00Z"));

Rotate a key

Create the replacement key

Create a new key with the same scopes or tighter ones:

curl -X POST "http://localhost:5001/api-keys" \
  -H "X-blnk-key: <api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Payments Service",
    "owner": "payments-team",
    "scopes": ["transactions:write", "balances:read"],
    "expires_at": "2027-06-13T00:00:00Z"
  }'
const response = await blnk.ApiKeys.create({
  name: 'Payments Service',
  owner: 'payments-team',
  scopes: ['transactions:write', 'balances:read'],
  expires_at: '2027-06-13T00:00:00Z',
});
expiresAt, _ := time.Parse(time.RFC3339, "2027-06-13T00:00:00Z")
apiKey, _, _ := client.ApiKeys.Create(blnkgo.CreateApiKeyRequest{
    Name: "Payments Service",
    Owner: "payments-team",
    Scopes: []string{"transactions:write", "balances:read"},
    ExpiresAt: expiresAt,
})
response = blnk.api_keys.create({
  "name": "Payments Service",
  "owner": "payments-team",
  "scopes": ["transactions:write", "balances:read"],
  "expires_at": "2027-06-13T00:00:00Z",
})
ApiResponse<JsonNode> response = blnk.apiKeys().create(
    CreateApiKeyData.create()
        .name("Payments Service")
        .owner("payments-team")
        .scopes(List.of("transactions:write", "balances:read"))
        .expiresAt("2027-06-13T00:00:00Z"));

Copy the plaintext key value from the response immediately. You won’t see it again.

Update your applications

Deploy the new key to your secret manager or environment variables. Verify the service works with the new key.

Revoke the old key

Delete the old key:

curl -X DELETE "http://localhost:5001/api-keys/api_key_879f0ecb-e29f-4137-801b-1048366381db?owner=payments-team" \
  -H "X-blnk-key: <api-key>"
const response = await blnk.ApiKeys.delete(
  'api_key_879f0ecb-e29f-4137-801b-1048366381db',
  {
    owner: 'payments-team',
  },
);
resp, _ := client.ApiKeys.Delete(
    "api_key_879f0ecb-e29f-4137-801b-1048366381db",
    &blnkgo.DeleteApiKeysOptions{
        Owner: "payments-team",
    },
)
response = blnk.api_keys.delete(
  "api_key_879f0ecb-e29f-4137-801b-1048366381db",
  {
    "owner": "payments-team",
  },
)
ApiResponse<JsonNode> response = blnk.apiKeys().delete(
    "api_key_879f0ecb-e29f-4137-801b-1048366381db",
    DeleteApiKeyOptions.create()
        .owner("payments-team"));

Confirm your applications no longer reference the old key.

Audit

List keys for the owner and confirm only the expected keys remain active:

curl -X GET "http://localhost:5001/api-keys?owner=payments-team" \
  -H "X-blnk-key: <api-key>"
const response = await blnk.ApiKeys.list({
  owner: 'payments-team',
});
keys, _, _ := client.ApiKeys.List(&blnkgo.ListApiKeysOptions{
    Owner: "payments-team",
})
response = blnk.api_keys.list({
  "owner": "payments-team",
})
ApiResponse<JsonNode> response = blnk.apiKeys().list(
    ListApiKeysOptions.create()
        .owner("payments-team"));

Security best practices

  • Review your key list regularly. Look for keys with broad scopes, keys that haven’t been used recently, and keys approaching their expiry date.
  • Create a separate key for each service or environment.
  • Set expiration dates and grant the minimum scopes. See Scopes before each create.
  • Store keys in a secret manager. Never commit them to version control.

Error handling

When a list, create, or revoke request fails validation or owner checks, Blnk returns 400 Bad Request or 404 Not Found.

CodeWhen it happens
APIKEY_INVALIDThe create request failed validation.
APIKEY_OWNER_REQUIREDThe master key was used to create or list keys without an owner parameter.
APIKEY_NOT_FOUNDThe key ID is not found in the caller’s owner context.
{
  "error": "owner is required",
  "error_detail": {
    "code": "APIKEY_OWNER_REQUIRED",
    "message": "owner is required"
  }
}

To resolve the error:

CodeWhat to do
APIKEY_OWNER_REQUIREDAdd the owner query parameter or body field when using the master key.
APIKEY_INVALIDFix the validation issue in the create request (missing fields, invalid scopes, or malformed dates).
APIKEY_NOT_FOUNDVerify the key ID and that it belongs to the owner you’re acting on. See Owner context.

Delegation and cross-owner errors are covered in Owner context. Permission errors for missing scopes are covered in Scopes.