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

# Impersonate

Mint a short-lived access token that acts as a service account, on behalf of the human calling this
endpoint — see [Service Accounts](/general/service-accounts) for the concept end to end.

## **POST** `/auth/core/impersonate`

### Request Body

| Parameter | Type   | Required | Description                              |
| :-------- | :----- | :------- | :--------------------------------------- |
| `user_id` | string | Yes      | The ID of the service account to act as. |

### Response

| Parameter      | Type    | Description                                                |
| :------------- | :------ | :--------------------------------------------------------- |
| `access_token` | string  | Short-lived JWT that authenticates as the service account. |
| `expires_at`   | integer | Epoch **milliseconds** when the token expires.             |

<Note>
  There is no refresh token. The token is single-shot by design — see [Token Lifetime](#token-lifetime).
</Note>

### Errors

| Status | Error                   | Cause                                                               |
| :----- | :---------------------- | :------------------------------------------------------------------ |
| 400    | `invalid_request_body`  | Malformed or missing `user_id`, or an extra field.                  |
| 400    | `not_a_service_account` | `user_id` does not resolve to a service account.                    |
| 400    | `account_retired`       | The service account has already been retired.                       |
| 403    | `forbidden`             | Not a user-scoped human, or lacks `admin` over the account's owner. |

See [Permissions](#permissions) below for who can call this endpoint at all.

### Example

<CodeGroup>
  ```bash Request theme={null}
  curl -X POST https://api.blobhub.io/v1/auth/core/impersonate \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "user_id": "fe827336-3548-429c-a37c-a06d9b689eac"
    }'
  ```

  ```json Response theme={null}
  {
    "status": "success",
    "access_token": "eyJhbGciOiJIUzI1Ni...",
    "expires_at": 1785795055908
  }
  ```
</CodeGroup>

### Permissions

| Caller                                                             | Access                                           |
| :----------------------------------------------------------------- | :----------------------------------------------- |
| The owning user, signed in with a bearer token                     | Allowed.                                         |
| An admin of the owning organization, signed in with a bearer token | Allowed.                                         |
| An org-scoped API key                                              | Forbidden — even with the `admin` role.          |
| A session already acting as a service account                      | Forbidden — an acting token cannot mint another. |
| The service account's own key                                      | Forbidden, always.                               |

Impersonation is the one operation on this page an org-scoped API key can never perform, unlike
[Create Account](/rest-api/users/create-account), [List Accounts](/rest-api/users/list-accounts) and
[Delete User](/rest-api/users/delete-user): minting a token means naming one specific human as the
actor, and a bare API key names no one.

### Why This Endpoint Exists

The token this endpoint mints is the **only** way past the
[access perimeter](/general/service-accounts#the-access-perimeter) — the ten operations a service account's
own key can never perform, from granting a membership to publishing a Blob. Every one of them asks whether
there is a human behind the request; a key answers no, and a token minted here answers yes and names them.

One narrow exception: minting an API key at an **org or blob** target is refused while acting, with `400`
[`cannot_mint_while_acting`](/rest-api/shared/create-api-key#errors), because such a key authenticates as
whoever mints it and would record the impersonated account rather than the human. Mint those as yourself.
Minting an account **its own** key, at `target=user`, is admitted — and is the whole point of a token from
here.

Minting one is itself on that perimeter, which is what keeps the hatch unreachable from inside: a service
account's key cannot mint the first token, and an acting token cannot mint a second, so a human starts every
chain. Whatever the token then does is attributable to the human named in it, for its full 60 minutes.

### Token Lifetime

The token lasts **60 minutes** and is **single-shot** — minting it creates nothing a second call
could revoke. Because it is a stateless JWT, there is no server-side state to clear early: the
60-minute expiry is the only control there is. Treat a minted token as sensitive for its full
lifetime, the same as any bearer token.
