# Me

> The calling principal, its sessions, passkeys and personal access tokens.

The calling principal, its sessions, passkeys and personal access tokens.

| Operation | Method and path |
| --- | --- |
| [Who am I](#who-am-i) | `GET /v1/me` |
| [Update my profile](#update-my-profile) | `PATCH /v1/me` |
| [List my sessions](#list-my-sessions) | `GET /v1/me/sessions` |
| [Sign out a session](#sign-out-a-session) | `DELETE /v1/me/sessions/{session_id}` |
| [List my passkeys](#list-my-passkeys) | `GET /v1/me/passkeys` |
| [Remove a passkey](#remove-a-passkey) | `DELETE /v1/me/passkeys/{passkey_id}` |
| [List my personal access tokens](#list-my-personal-access-tokens) | `GET /v1/me/tokens` |
| [Create a personal access token](#create-a-personal-access-token) | `POST /v1/me/tokens` |
| [Revoke a personal access token](#revoke-a-personal-access-token) | `DELETE /v1/me/tokens/{pat_id}` |

## Who am I

```http
GET /v1/me
```

- **Operation:** `getMe`
- **Scopes:** any signed-in principal
- **CLI:** `guv whoami`

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. | [Me](/docs/api/objects/me/) |

**Errors:** `400`, `401`, `403`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/me \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "principal": {
    "type": "user",
    "id": "string"
  },
  "user": {
    "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
    "email": "stewart@primecarers.co.uk",
    "name": "Stewart Webb",
    "avatar_url": null,
    "github_login": "stewartwebb",
    "has_passkey": true,
    "is_platform_admin": true,
    "default_workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
    "created_at": "2026-10-22T09:00:00Z"
  },
  "agent_token": {
    "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk",
    "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
    "name": "claude-code-on-stewart-laptop",
    "description": "Claude Code on Stewart's MacBook",
    "prefix": "guv_agt_Qm4x",
    "scopes": [
      "projects:read",
      "deploy:preview",
      "deploy:staging",
      "logs:read",
      "secrets:write-only",
      "db:branch"
    ],
    "projects": null,
    "environments": [
      "preview",
      "staging"
    ],
    "budget": {
      "spend_increase_per_day": {
        "amount": "10.00",
        "currency": "GBP",
        "display": "£10.00"
      }
    },
    "approval_channel": [
      "push",
      "email"
    ],
    "status": "active",
    "summary": [
      "Can deploy to staging and previews.",
      "Cannot touch production without your approval.",
      "Can set secrets but never read them.",
      "Can raise spend by up to £10.00 a day."
    ],
    "created_by": {
      "type": "user",
      "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
      "display": "Stewart Webb"
    },
    "expires_at": "2026-11-22T09:00:00Z",
    "last_used_at": "2026-10-23T13:58:00Z",
    "revoked_at": null,
    "created_at": "2026-10-23T09:00:00Z"
  },
  "scopes": [
    "deploy:staging"
  ],
  "workspaces": [
    {
      "id": "string",
      "slug": "string",
      "name": "string",
      "role": "owner",
      "plan": "trial"
    }
  ],
  "csrf_token": "string",
  "session_expires_at": "2026-10-22T13:05:00Z"
}
```

## Update my profile

```http
PATCH /v1/me
```

- **Operation:** `updateMe`
- **Scopes:** any signed-in principal
- **Idempotency-Key:** required; supports `?dry_run=true`
- **Concurrency:** send the `ETag` you read as `If-Match`

**Request body** (`application/json`, required): [UserUpdate](/docs/api/objects/user-update/)

| Field | Type | Description |
| --- | --- | --- |
| `name` | string |  |
| `default_workspace_id` | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | OK. With `dry_run=true`: a Plan (application/vnd.guv.plan+json) and nothing changes. | [User](/docs/api/objects/user/) |

**Errors:** `400`, `401`, `403`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X PATCH https://api.guv.dev/v1/me \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "default_workspace_id": "string"
}'
```

**Example response**

```json
{
  "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us",
  "email": "stewart@primecarers.co.uk",
  "name": "Stewart Webb",
  "avatar_url": null,
  "github_login": "stewartwebb",
  "has_passkey": true,
  "is_platform_admin": true,
  "default_workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws",
  "created_at": "2026-10-22T09:00:00Z"
}
```

## List my sessions

```http
GET /v1/me/sessions
```

- **Operation:** `listMySessions`
- **Scopes:** any signed-in principal
- **Paginated:** `?limit=` and `?cursor=`

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of SessionInfo objects. | [SessionInfoList](/docs/api/objects/session-info-list/) |

**Errors:** `400`, `401`, `403`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/me/sessions \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "ses_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "kind": "browser",
      "auth_method": "passkey",
      "user_agent": "string",
      "ip": "string",
      "current": false,
      "created_at": "2026-10-22T13:05:00Z",
      "last_seen_at": "2026-10-22T13:05:00Z",
      "expires_at": "2026-10-22T13:05:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Sign out a session

```http
DELETE /v1/me/sessions/{session_id}
```

- **Operation:** `revokeMySession`
- **Scopes:** any signed-in principal
- **Idempotency-Key:** required; supports `?dry_run=true`
- **Concurrency:** send the `ETag` you read as `If-Match`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `session_id` * | path | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `204` No Content | Done. No body. | none |

**Errors:** `400`, `401`, `403`, `404`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X DELETE https://api.guv.dev/v1/me/sessions/ses_01jbx2m9k7q4r8t5v6w3y0z1pr \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

## List my passkeys

```http
GET /v1/me/passkeys
```

- **Operation:** `listMyPasskeys`
- **Scopes:** any signed-in principal
- **Paginated:** `?limit=` and `?cursor=`

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of Passkey objects. | [PasskeyList](/docs/api/objects/passkey-list/) |

**Errors:** `400`, `401`, `403`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/me/passkeys \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "pk_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "name": "string",
      "aaguid": "string",
      "backed_up": false,
      "created_at": "2026-10-22T13:05:00Z",
      "last_used_at": "2026-10-22T13:05:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Remove a passkey

```http
DELETE /v1/me/passkeys/{passkey_id}
```

Refused when it is the last passkey on an account in a workspace that requires passkeys.

- **Operation:** `deleteMyPasskey`
- **Scopes:** any signed-in principal
- **Idempotency-Key:** required; supports `?dry_run=true`
- **Concurrency:** send the `ETag` you read as `If-Match`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `passkey_id` * | path | string |  |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `204` No Content | Done. No body. | none |

**Errors:** `400`, `401`, `403`, `404`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X DELETE https://api.guv.dev/v1/me/passkeys/pas_01jbx2m9k7q4r8t5v6w3y0z1pr \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

## List my personal access tokens

```http
GET /v1/me/tokens
```

- **Operation:** `listMyTokens`
- **Scopes:** `tokens:read`
- **Paginated:** `?limit=` and `?cursor=`

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | A page of PersonalAccessToken objects. | [PersonalAccessTokenList](/docs/api/objects/personal-access-token-list/) |

**Errors:** `400`, `401`, `403`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl https://api.guv.dev/v1/me/tokens \
  -H "Authorization: Bearer $GUV_TOKEN"
```

**Example response**

```json
{
  "data": [
    {
      "id": "pat_01jbx2m9k7q4r8t5v6w3y0z1pr",
      "name": "string",
      "prefix": "guv_pat_7Hc2",
      "workspace_id": "string",
      "scopes": [
        "deploy:staging"
      ],
      "expires_at": "2026-10-22T13:05:00Z",
      "last_used_at": "2026-10-22T13:05:00Z",
      "created_at": "2026-10-22T13:05:00Z"
    }
  ],
  "next_cursor": "string",
  "has_more": false
}
```

## Create a personal access token

```http
POST /v1/me/tokens
```

The token is returned once.

- **Operation:** `createMyToken`
- **Scopes:** `tokens:write`
- **Idempotency-Key:** required; supports `?dry_run=true`

**Request body** (`application/json`, required): [PersonalAccessTokenCreate](/docs/api/objects/personal-access-token-create/)

| Field | Type | Description |
| --- | --- | --- |
| `name` * | string |  |
| `workspace_id` | string |  |
| `scopes` * | array of [Scope](/docs/api/objects/scope/) |  |
| `expires_in` | string | Default `90d`. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `201` Created | Created. | [PersonalAccessTokenCreated](/docs/api/objects/personal-access-token-created/) |

**Errors:** `400`, `401`, `403`, `409`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X POST https://api.guv.dev/v1/me/tokens \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "workspace_id": "string",
  "scopes": [
    "deploy:staging"
  ],
  "expires_in": "90d"
}'
```

**Example response**

```json
{
  "id": "pat_01jbx2m9k7q4r8t5v6w3y0z1pr",
  "name": "string",
  "prefix": "guv_pat_7Hc2",
  "workspace_id": "string",
  "scopes": [
    "deploy:staging"
  ],
  "expires_at": "2026-10-22T13:05:00Z",
  "last_used_at": "2026-10-22T13:05:00Z",
  "created_at": "2026-10-22T13:05:00Z",
  "token": "string"
}
```

## Revoke a personal access token

```http
DELETE /v1/me/tokens/{pat_id}
```

- **Operation:** `revokeMyToken`
- **Scopes:** `tokens:write`
- **Idempotency-Key:** required; supports `?dry_run=true`
- **Concurrency:** send the `ETag` you read as `If-Match`

**Parameters**

| Name | In | Type | Description |
| --- | --- | --- | --- |
| `pat_id` * | path | string | Personal access token id. |

**Responses**

| Status | Meaning | Body |
| --- | --- | --- |
| `200` OK | Dry run. Nothing was changed; this is what would happen. | [Plan](/docs/api/objects/plan/) |
| `204` No Content | Done. No body. | none |

**Errors:** `400`, `401`, `403`, `404`, `409`, `412`, `422`, `429`, `500`, as [problem+json](/errors/) with a stable `code`.

**Example**

```bash
curl -X DELETE https://api.guv.dev/v1/me/tokens/pat_01jbx2m9k7q4r8t5v6w3y0z1pr \
  -H "Authorization: Bearer $GUV_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```
