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

# Create, roll and revoke API keys

> Create a key with the scopes and expiry you want, store the secret, replace it without downtime, and kill it when you need to.

export const UiPath = ({children}) => {
  const parts = String(children).split(/\s*(?:→|>)\s*/).filter(Boolean);
  return <span className="pl-path">
      {parts.map((part, index) => <span key={`${index}-${part}`}>
          {index > 0 && <span className="pl-path__sep">→</span>}
          {part}
        </span>)}
    </span>;
};

export const Screenshot = ({id, alt, caption, frame = 'browser', url, marks = [], lang = 'en', workspace = 'northwind-outbound'}) => {
  const src = `/images/screenshots/${lang}/${id}.png`;
  const chrome = url ? `app.pipelime.ai/${workspace}${url}` : 'app.pipelime.ai';
  return <figure className={`pl-shot pl-shot--${frame} not-prose`}>
      <div className="pl-shot__frame">
        {frame !== 'bare' && <div className="pl-shot__bar">
            <span className="pl-shot__dots">
              <span className="pl-shot__dot" />
              <span className="pl-shot__dot" />
              <span className="pl-shot__dot" />
            </span>
            <span className="pl-shot__url">{chrome}</span>
          </div>}
        <div className="pl-shot__media">
          <img src={src} alt={alt} loading="lazy" />
          {marks.map(mark => <span key={mark.n} className="pl-shot__mark" style={{
    left: `${mark.x}%`,
    top: `${mark.y}%`
  }} aria-hidden="true">
              {mark.n}
            </span>)}
        </div>
      </div>
      {caption && <figcaption className="pl-shot__caption">{caption}</figcaption>}
    </figure>;
};

By the end of this page you can create an API key with the permissions and expiry you want, replace it without any downtime, and shut it off the moment you need to.

<div className="pl-availability">
  <div className="pl-availability__row">
    <div className="pl-availability__label">Where</div>
    <div className="pl-availability__value"><span className="pl-path">Settings<span className="pl-path__sep">→</span>API & Integrations</span></div>
  </div>

  <div className="pl-availability__row">
    <div className="pl-availability__label">Your role needs</div>
    <div className="pl-availability__value">Update access to API & Integrations <code>update-companies</code>. Admin have it by default.</div>
  </div>

  <div className="pl-availability__note">If you cannot find this in your sidebar, your workspace may have a custom menu configuration. Contact support and we will check it for you.</div>
</div>

## Before you begin

* Decide which system will call the API with this key (a website form, an internal script, a workflow tool) and name the key after it. The name is what makes it safe to revoke later, and this screen has no rename control, so the name you pick is the one it keeps.
* Have somewhere to put the secret before you start. It is shown once, in the dialog that opens the moment the key exists.

## Steps

<Steps>
  <Step title="Open API & Integrations">
    Go to <UiPath>Settings → API & Integrations</UiPath>. On a workspace that has never had a key, the list is replaced by a short explanation of what a key is for. Both the button in that block and the one in the card header say **Create key**, and both open the same dialog.

    <Screenshot id="api/api-keys--empty" url="/settings/api" alt="The API keys card with no keys yet: a Create key button in the header, and below it a key icon, the line 'No API keys yet' and a second Create key button" caption="Once the first key exists, the middle of this card becomes the list of keys." />
  </Step>

  <Step title="Create the key">
    Select **Create key** to open the dialog, and give the key a name. Name it after the system that will use it. That is what makes it safe to revoke later.

    Under **Permissions**, tick what this integration needs. Two are ticked for you: `leads:write`, which creates and updates leads, and `tags:read`, which lists tags. The third, `leads:read`, is not. Add it if the integration also has to read records back. At least one has to stay ticked.

    Under **Expiry**, **No expiry** is preselected. The alternatives are **30 days**, **90 days** and **1 year**, each counted from the moment you create the key.

    Select **Create key** in the dialog to confirm.

    <Tip>
      The secret starts with a prefix that names the environment that issued it, so a key from a test environment cannot be mistaken for a live one. Paste the wrong one into an integration and it fails at the first request instead of quietly writing to somewhere else.
    </Tip>
  </Step>

  <Step title="Copy the secret">
    The dialog that opens carries the full key, and says at the top that this is the only time it can be shown. We store a one-way hash of the secret rather than the secret itself, so once you close the dialog no screen can show it again, to you or to us.

    Copy the key from the code block (1), which has a **Copy** button in its header strip. Then tick **I've copied and stored this key somewhere safe** (2). **Done** stays disabled until you do, and the dialog ignores both a click outside it and the Escape key.

    <Screenshot id="api/api-keys--secret" url="/settings/api" alt="The one-time secret dialog: a warning that this is the only time the key can be shown, the full key in a dark code block, and a checkbox to confirm before Done unlocks" caption="A representative example, captured against a test key. If you lose the secret before pasting it anywhere, rolling the key is the only way back." marks={[{ n: 1, x: 50, y: 47 }, { n: 2, x: 20, y: 70 }]} />

    <Note>
      While you stay on this page, the key you just created also fills the request examples and the **Try it** panel below the list, so you can fire a first request without pasting anything. It is held in the page and never stored, so leaving the page loses it too.
    </Note>
  </Step>

  <Step title="Find it again in the list">
    Each row opens with the key's name and a status dot reading **Active**, **Expired** or **Revoked**. Under that comes the masked key (1): its public prefix, an ellipsis, then the last four characters of the secret. Then follow the permissions it carries and when it was last used, or "never used" if it has not been. While a key is **Active** and has an expiry, the date it expires closes that line.

    Revoked keys sink to the bottom of the list and are dimmed there (2). Everything else is ordered newest first.

    <Screenshot id="api/api-keys--list" url="/settings/api" alt="Two API keys in the list: an active key whose second line shows the masked key, its scopes and a last-used date, and a dimmed revoked key below it" caption="The masked key is safe on screen: the rest of the secret was never stored, here or anywhere else." marks={[{ n: 1, x: 15, y: 32 }, { n: 2, x: 15, y: 68 }]} />
  </Step>

  <Step title="Roll a key to replace it">
    Open the row's overflow menu (the icon at the end of the row, tooltip **More**) and choose **Roll this key**. A replacement is issued immediately and the one-time dialog reopens with its secret, naming the old key by its masked value so you can tell the two apart.

    The replacement is a new key that copies the old one's name, permissions and expiry. Two rows now carry the same name, and the masked prefix is what separates them. The expiry it copies is the original date, not a fresh window: roll a key that expires next Tuesday and the replacement expires next Tuesday too.

    Nothing about the old key changes until you revoke it, so your integration keeps working while you swap the new secret in.
  </Step>

  <Step title="Revoke a key">
    From the same menu, choose **Revoke**. Your browser asks you to confirm, warning that any integration using the key stops working immediately and that this cannot be undone. Confirm, and the panel reports "API key revoked."

    Revoking never deletes the row. It stays in the list, dimmed, as the only record that the key existed and when it was last used.

    <Warning>
      A key acts as the person who created it. Delete that user account and the key is orphaned: every call it makes fails with a 401 telling the caller to create a new key. Removing that person from this workspace does not do that. Their account still exists, so their keys keep working, and a key you want stopped has to be revoked deliberately.

      Rolling makes whoever performs the roll the key's new owner, carrying the name, permissions and expiry across. That is how you move a key off an account that is going away. But the replacement has a new secret, so the integration still has to be updated before the old key is revoked.
    </Warning>
  </Step>
</Steps>

## What happens next

<Check>
  The new key sits at the top of the list as **Active**, carrying the name, permissions and expiry you chose, and it authenticates requests the moment it exists. There is nothing further to switch on. Reload the panel after the integration's first call and "never used" is replaced by a last-used date.
</Check>

A key with an expiry stops authenticating on its own once that date passes, and its status becomes **Expired** without anyone revoking it. **Roll this key** and **Revoke** are greyed out on any key that is not **Active**, so an expired key is a dead end: create a new one.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Every request from this integration fails with 401">
    Read the message in the response body. "This API key is orphaned because the user who created it no longer exists. Create a new key." means the account that created the key was deleted. The key has nobody to act as, so it is treated as dead rather than silently promoted. Anything else returns one generic "Invalid, revoked or expired API key.", deliberately, so the endpoint cannot be used to probe which keys exist. Check the key you deployed against the masked prefixes in the list, and check its status there.

    Either way the fix is a new key: an existing one cannot be reassigned to a different person after the fact. Before it happens again, have someone else roll any key whose owner is leaving, and deploy the replacement secret.
  </Accordion>

  <Accordion title="Roll and Revoke are both greyed out on this key">
    Both need the key to be **Active**. Once a key is **Expired** or **Revoked**, the menu disables them and there is nothing left to do to that key. Create a new one instead.
  </Accordion>

  <Accordion title="I closed the secret dialog before copying the key">
    There is no way to see a key's secret a second time. Only a one-way hash of it is ever stored, on purpose, so a database leak yields no usable credential. Open the row's overflow menu and choose **Roll this key** to issue a replacement. The old key keeps working until you revoke it, so nothing breaks while you swap the new one in.
  </Accordion>
</AccordionGroup>

## Related

<CardGroup cols={2}>
  <Card title="API overview" icon="compass" href="/en/developers/overview">
    What the public API is for, how it authenticates, and what a permission grants.
  </Card>

  <Card title="Sessions, API keys and revoking access" icon="shield" href="/en/security/sessions-api-keys-and-revocation">
    Every credential the product holds, and the switch that kills each one.
  </Card>

  <Card title="Errors reference" icon="circle-alert" href="/en/developers/errors">
    Every status the API returns and what to change before retrying.
  </Card>
</CardGroup>
