---
title: "Complete consent (console only)"
method: POST
path: "/oauth/consent"
tags: ["OAuth"]
---

# Complete consent (console only)

`POST /oauth/consent`

## Request

- Base URL: `https://api.infolang.ai`
- URL: `https://api.infolang.ai/oauth/consent`
- Auth: HTTP bearer

Called by the console once a human approves, authenticated with their **session JWT** — the only session-gated piece of the OAuth surface. Mints a one-time PKCE authorization code and returns where to send the browser.

**A user cannot consent to more than they hold.** Every workspace in `workspaces` must be one the calling principal has a grant on (403 otherwise), and the approved `scopes` must be within what their roles on those workspaces permit. `max_role` is a ceiling only: Kairos takes `min(grant role, max_role)` per workspace, so naming a role higher than the user's does not elevate anything.

Send `deny: true` to refuse — the response redirects the client to its own `redirect_uri` with `error=access_denied`, and no code is minted.

## Request body

- object
  - `request` string, required
  - `deny` boolean
  - `workspaces` string[]
  - `scopes` string[]
  - `max_role` 'Owner' | 'Admin' | 'Member' | 'ReadOnly'

## Response `200`

Where the console should send the browser

- object
  - `redirect_to` string, required
  - `expires_in` number

## Other responses

- `401`: Missing or invalid session JWT
- `403`: `forbidden` (workspace not held) or `insufficient_scope`
- `422`: Malformed or expired request handle, or unknown client

---

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