# Approvals API

Production approvals for AI clients. 3 operations under /api/v1. Errors are RFC 9457 problem+json with a stable `code` field. The complete schemas are in https://nebulactrl.com/api/v1/openapi.json.

## GET /approvals/{approvalId}

Get a production approval request

The plan an AI client's production change needs a person to approve: what will happen, who asked (token and MCP client), when it expires and whether you can decide. Signed-in members who can read the project only; tokens are refused.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `approvalId` (path, string, required)

Response 200: OK (ApprovalView)
  - `canDecide` (boolean, required)
  - `createdAt` (string (date-time), required)
  - `decidedAt` (string (date-time))
  - `decidedBy` (string): Name of the person who decided
  - `decidedVia` (enum: web | elicitation)
  - `environmentId` (string, required)
  - `expiresAt` (string (date-time), required)
  - `id` (string, required)
  - `plan` (ApprovalPlan, required)
  - `projectId` (string, required)
  - `requester` (ApprovalRequester, required)
  - `status` (enum: pending | approved | declined | used | expired | stale, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 422 Unprocessable Entity; 500 Internal Server Error

## POST /approvals/{approvalId}/approve

Approve a production change

Lets the waiting call proceed once, with exactly the plan shown. Owner, Admin or Developer with deploy rights on the project, signed in. The approval is valid for ten minutes from the request and void when the service changes in the meantime.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `approvalId` (path, string, required)

Response 200: OK (ApprovalView)
  - `canDecide` (boolean, required)
  - `createdAt` (string (date-time), required)
  - `decidedAt` (string (date-time))
  - `decidedBy` (string): Name of the person who decided
  - `decidedVia` (enum: web | elicitation)
  - `environmentId` (string, required)
  - `expiresAt` (string (date-time), required)
  - `id` (string, required)
  - `plan` (ApprovalPlan, required)
  - `projectId` (string, required)
  - `requester` (ApprovalRequester, required)
  - `status` (enum: pending | approved | declined | used | expired | stale, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict; 422 Unprocessable Entity; 500 Internal Server Error

## POST /approvals/{approvalId}/decline

Decline a production change

Refuses the waiting call. The same call stays refused for one minute. Same permissions as approving.

Authentication: session cookie or `Authorization: Bearer nct_…` token.

Parameters:
- `approvalId` (path, string, required)

Response 200: OK (ApprovalView)
  - `canDecide` (boolean, required)
  - `createdAt` (string (date-time), required)
  - `decidedAt` (string (date-time))
  - `decidedBy` (string): Name of the person who decided
  - `decidedVia` (enum: web | elicitation)
  - `environmentId` (string, required)
  - `expiresAt` (string (date-time), required)
  - `id` (string, required)
  - `plan` (ApprovalPlan, required)
  - `projectId` (string, required)
  - `requester` (ApprovalRequester, required)
  - `status` (enum: pending | approved | declined | used | expired | stale, required)
Errors: 401 Unauthorized; 403 Forbidden; 404 Not Found; 409 Conflict; 422 Unprocessable Entity; 500 Internal Server Error
