---
title: "Approve Request"
method: POST
path: "/agent-keys/requests/{request_id}/approve"
tags: ["agent-keys"]
---

# Approve Request

`POST /agent-keys/requests/{request_id}/approve`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/agent-keys/requests/{request_id}/approve`
- Auth: none declared

Approve, choosing the scope. **Re-verifies the password.**

Fresh authentication, not merely a session — and a password re-verify rather than a
token-age test, because the access token carries `iat` and no `auth_time` and refresh
mints fresh access tokens from the cookie indefinitely. `now - iat < 5 min` is
therefore true for any SPA tab that has been open and refreshing, i.e. exactly the
stale session the control exists to reject; a max-age framing would degrade silently
to "has a session". `POST /users/me/change-password` is the primitive this codebase
already has, and this is modelled on it.

**The typed `user_code` is gone; `approve_pairing` asserts the row's address against
this session instead.** That assertion is not a formality and it lives in the service
rather than here: `/connect` lists by address, so the request id is obtainable by
anyone who can read the mailbox — and shared mailboxes (`buchhaltung@`, `info@`) are
the norm among the customers this serves. A colleague opening a notice aimed at
somebody else's account would otherwise sign in as themselves, re-verify their own
password, pick their own company, and hand the requester a key to books nobody meant
to grant.

**`full` can never be granted here** — refused by the schema and again below. One
phished approval must not hand over everything; full-access stays browser-only.

## Path parameters

- `request_id` string, required

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Request body

- PairingApproveIn: The approval. **Carries the password, and that is the fresh-auth control.** Not a token-age test: the access token carries `iat` and no `auth_time`, and refresh mints fresh access tokens from the cookie indefinitely — so `now - iat < 5 min` is true for any SPA tab that has been open, i.e. exactly the stale session the control exists to reject. A max-age framing would degrade silently to "has a session".
  - `password` string, required
  - `purpose` string, required
  - `company_id` string, nullable

## Response `200`

Successful Response

- PairingApproveOut: Approval's answer. **May carry the key itself** — the manual fallback. If the agent is gone (context compacted, client restarted) the page shows the key once. Having the user paste a *secret* so the agent can exchange it for a key adds a round trip and no security: both are bearer credentials.
  - `status` string
  - `api_key` string, nullable
  - `purpose` string, nullable
  - `company_id` string, nullable

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
