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

# Manage API Keys

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

export const CtaCallout = props => {
  const {title, buttonLabel, href, trackingEvent, buttonTarget, rel = "noopener noreferrer", children} = props;
  const handleCtaClick = () => {
    if (typeof window === "undefined" || !trackingEvent) {
      return;
    }
    try {
      window.dispatchEvent(new CustomEvent("blnk:docs-cta", {
        detail: {
          name: trackingEvent,
          href
        }
      }));
    } catch {}
    try {
      window.posthog?.capture?.(trackingEvent, {
        href
      });
    } catch {}
    const gaPayload = {
      cta_href: href
    };
    try {
      window.gtag?.("event", trackingEvent, gaPayload);
    } catch {}
    try {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: trackingEvent,
        ...gaPayload
      });
    } catch {}
  };
  const isExternal = typeof href === "string" && (/^https?:\/\//i).test(href);
  const target = buttonTarget ?? (isExternal ? "_blank" : undefined);
  const linkRel = isExternal ? rel : undefined;
  return <section className="cta-callout not-prose relative my-8 w-full min-w-0 overflow-hidden rounded-xl border border-zinc-200 p-5 dark:border-white/10">
      <div className="cta-callout-noise" aria-hidden="true" />
      <div className="cta-callout-layout">
        {title ? <div className="cta-callout-title-row">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="14" height="14" className="cta-callout-icon shrink-0 text-zinc-800 dark:text-zinc-200" aria-hidden="true">
              <g fill="none" fillRule="nonzero">
                <path d="M28 0v28H0V0h28ZM14.691833333333335 27.134333333333334l-0.012833333333333334 0.0023333333333333335 -0.08283333333333333 0.04083333333333334 -0.023333333333333334 0.004666666666666667 -0.016333333333333335 -0.004666666666666667 -0.08283333333333333 -0.04083333333333334c-0.011666666666666667 -0.004666666666666667 -0.022166666666666668 -0.0011666666666666668 -0.028000000000000004 0.005833333333333334l-0.004666666666666667 0.011666666666666667 -0.019833333333333335 0.49933333333333335 0.005833333333333334 0.023333333333333334 0.011666666666666667 0.015166666666666667 0.12133333333333333 0.08633333333333333 0.0175 0.004666666666666667 0.014000000000000002 -0.004666666666666667 0.12133333333333333 -0.08633333333333333 0.014000000000000002 -0.018666666666666668 0.004666666666666667 -0.019833333333333335 -0.019833333333333335 -0.4981666666666667c-0.0023333333333333335 -0.011666666666666667 -0.0105 -0.019833333333333335 -0.019833333333333335 -0.021Zm0.3091666666666667 -0.13183333333333336 -0.015166666666666667 0.0023333333333333335 -0.21583333333333335 0.1085 -0.011666666666666667 0.011666666666666667 -0.0035000000000000005 0.012833333333333334 0.021 0.5016666666666667 0.005833333333333334 0.014000000000000002 0.009333333333333334 0.008166666666666668 0.23450000000000004 0.1085c0.014000000000000002 0.004666666666666667 0.026833333333333334 0 0.03383333333333334 -0.009333333333333334l0.004666666666666667 -0.016333333333333335 -0.03966666666666667 -0.7163333333333334c-0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.023333333333333334 -0.023333333333333334 -0.025666666666666667Zm-0.8341666666666667 0.0023333333333333335a0.026833333333333334 0.026833333333334334 0 0 0 -0.0315 0.007000000000000001l-0.007000000000000001 0.016333333333333335 -0.03966666666666667 0.7163333333333334c0 0.014000000000000002 0.008166666666666668 0.023333333333333334 0.019833333333333335 0.028000000000000004l0.0175 -0.0023333333333333335 0.23450000000000004 -0.1085 0.011666666666666667 -0.009333333333333334 0.004666666666666667 -0.012833333333333334 0.019833333333333335 -0.5016666666666667 -0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.011666666666666667 -0.21466666666666667 -0.10733333333333334Z" strokeWidth="1.1667" />
                <path fill="currentColor" d="M14 2.916666666666667A1.75 1.75 0 0 1 15.750000000000002 4.666666666666667v6.302333333333334L21.207666666666668 7.816666666666667a1.75 1.75 0 0 1 1.75 3.031L17.5 14l5.457666666666667 3.151166666666667a1.75 1.75 0 0 1 -1.75 3.031l-5.457666666666667 -3.1500000000000004V23.333333333333336a1.75 1.75 0 0 1 -3.5 0v-6.302333333333334L6.792333333333334 20.183333333333337a1.75 1.75 0 1 1 -1.75 -3.031L10.5 14 5.042333333333334 10.848833333333333a1.75 1.75 0 0 1 1.75 -3.031l5.457666666666667 3.1500000000000004V4.666666666666667A1.75 1.75 0 0 1 14 2.916666666666667Z" strokeWidth="1.1667" />
              </g>
            </svg>
            <p className="cta-callout-title min-w-0 font-semibold text-zinc-800 dark:text-zinc-200">
              {title}
            </p>
          </div> : null}
        <div className={`cta-callout-body text-sm leading-normal text-zinc-800 dark:text-zinc-200${title ? " cta-callout-body--indented" : ""}`}>
          {children}
        </div>
        <a href={href} target={target} rel={linkRel} onClick={handleCtaClick} data-docs-cta={trackingEvent || undefined} className="cta-callout-button inline-flex items-center justify-center gap-1 rounded-full bg-white px-3 py-1.5 text-sm font-semibold transition hover:bg-zinc-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/50 dark:bg-white dark:hover:bg-zinc-200">
          {buttonLabel}
          <span className="cta-callout-button-arrow" aria-hidden="true">
            →
          </span>
        </a>
      </div>
    </section>;
};

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](/api-keys/overview).

Listing, revoking, and delegating keys requires the master key or a scoped key with the matching `api-keys:*` scopes. See [Scopes](/api-keys/scopes) for permissions and [Owner context](/api-keys/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.

<CodeGroup>
  ```bash cURL wrap theme={"system"}
  curl -X GET "http://localhost:5001/api-keys?owner=payments-team" \
    -H "X-blnk-key: <api-key>"
  ```

  ```typescript TypeScript wrap theme={"system"}
  const response = await blnk.ApiKeys.list({
    owner: 'payments-team',
  });
  ```
</CodeGroup>

```json 200 OK wrap theme={"system"}
[
  {
    "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.

<CodeGroup>
  ```bash cURL wrap theme={"system"}
  curl -X DELETE "http://localhost:5001/api-keys/api_key_879f0ecb-e29f-4137-801b-1048366381db?owner=payments-team" \
    -H "X-blnk-key: <api-key>"
  ```

  ```typescript TypeScript wrap theme={"system"}
  const response = await blnk.ApiKeys.delete(
    'api_key_879f0ecb-e29f-4137-801b-1048366381db',
    {
      owner: 'payments-team',
    },
  );
  ```
</CodeGroup>

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

<Warning>
  Revoking a key takes effect immediately. Deploy a replacement key before revoking the old one.
</Warning>

***

## Delegate key creation

<Info>Available on Blnk Core 0.14.3 and later.</Info>

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](/api-keys/owner-context) for inheritance and cross-owner rules.

<CodeGroup>
  ```bash cURL wrap theme={"system"}
  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"
    }'
  ```

  ```typescript TypeScript wrap theme={"system"}
  const response = await blnk.ApiKeys.create({
    name: 'Nightly Reconciliation Job',
    owner: 'payments-team',
    scopes: ['reconciliation:read'],
    expires_at: '2027-01-01T00:00:00Z',
  });
  ```
</CodeGroup>

***

## Rotate a key

<Steps>
  <Step title="Create the replacement key">
    Create a new key with the same scopes or tighter ones:

    <CodeGroup>
      ```bash cURL wrap theme={"system"}
      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"
        }'
      ```

      ```typescript TypeScript wrap theme={"system"}
      const response = await blnk.ApiKeys.create({
        name: 'Payments Service',
        owner: 'payments-team',
        scopes: ['transactions:write', 'balances:read'],
        expires_at: '2027-06-13T00:00:00Z',
      });
      ```
    </CodeGroup>

    Copy the plaintext `key` value from the response immediately. You won't see it again.
  </Step>

  <Step title="Update your applications">
    Deploy the new key to your secret manager or environment variables. Verify the service works with the new key.
  </Step>

  <Step title="Revoke the old key">
    Delete the old key:

    <CodeGroup>
      ```bash cURL wrap theme={"system"}
      curl -X DELETE "http://localhost:5001/api-keys/api_key_879f0ecb-e29f-4137-801b-1048366381db?owner=payments-team" \
        -H "X-blnk-key: <api-key>"
      ```

      ```typescript TypeScript wrap theme={"system"}
      const response = await blnk.ApiKeys.delete(
        'api_key_879f0ecb-e29f-4137-801b-1048366381db',
        {
          owner: 'payments-team',
        },
      );
      ```
    </CodeGroup>

    Confirm your applications no longer reference the old key.
  </Step>

  <Step title="Audit">
    List keys for the owner and confirm only the expected keys remain active:

    <CodeGroup>
      ```bash cURL wrap theme={"system"}
      curl -X GET "http://localhost:5001/api-keys?owner=payments-team" \
        -H "X-blnk-key: <api-key>"
      ```

      ```typescript TypeScript wrap theme={"system"}
      const response = await blnk.ApiKeys.list({
        owner: 'payments-team',
      });
      ```
    </CodeGroup>
  </Step>
</Steps>

***

## 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](/api-keys/scopes) before each create.
* Store keys in a secret manager. Never commit them to version control.

***

## Error handling

<Info>
  Structured errors are available from Blnk Core 0.15.0 and later.
</Info>

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

| Code                    | When it happens                                                              |
| :---------------------- | :--------------------------------------------------------------------------- |
| `APIKEY_INVALID`        | The create request failed validation.                                        |
| `APIKEY_OWNER_REQUIRED` | The master key was used to create or list keys without an `owner` parameter. |
| `APIKEY_NOT_FOUND`      | The key ID is not found in the caller's owner context.                       |

```json 400 Bad Request wrap theme={"system"}
{
  "error": "owner is required",
  "error_detail": {
    "code": "APIKEY_OWNER_REQUIRED",
    "message": "owner is required"
  }
}
```

To resolve the error:

| Code                    | What to do                                                                                                                        |
| :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------- |
| `APIKEY_OWNER_REQUIRED` | Add the `owner` query parameter or body field when using the master key.                                                          |
| `APIKEY_INVALID`        | Fix the validation issue in the create request (missing fields, invalid scopes, or malformed dates).                              |
| `APIKEY_NOT_FOUND`      | Verify the key ID and that it belongs to the owner you're acting on. See [Owner context](/api-keys/owner-context#error-handling). |

Delegation and cross-owner errors are covered in [Owner context](/api-keys/owner-context#error-handling). Permission errors for missing scopes are covered in [Scopes](/api-keys/scopes#error-handling).

***

## Related docs

<CardGroup cols={2}>
  <Card title="Overview" icon="lock-keyhole" href="/api-keys/overview">
    Create and use scoped keys.
  </Card>

  <Card title="Scopes" icon="list-check" href="/api-keys/scopes">
    Pick the right permissions.
  </Card>

  <Card title="Owner context" icon="users" href="/api-keys/owner-context">
    How Blnk isolates key management.
  </Card>
</CardGroup>

***

## Need help?

We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool.

To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx).

<CtaCallout title="Connect your ledger to Blnk Cloud" href="https://cloud.blnkfinance.com/auth/sign-up?utm_source=blnk_docs&utm_medium=documentation&utm_campaign=need-help" buttonLabel="Open Blnk Cloud" trackingEvent="clicked_cloud_signup">
  Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard.
</CtaCallout>
