---
title: "Review a publishing approval request"
method: POST
path: "/projects/publishing-requests/{requestId}/reviews"
tags: ["Approval requests"]
---

# Review a publishing approval request

`POST /projects/publishing-requests/{requestId}/reviews`

## Request

- Base URL: `https://api.replit.com/v1`
- URL: `https://api.replit.com/v1/projects/publishing-requests/{requestId}/reviews`
- Auth: HTTP bearer

Approve or deny one pending publishing approval request owned by this
Account. The API key's creator must retain approval-management access for
the Account.

Approval starts publishing the Project on behalf of the original requester.
The review body contains only the decision and, for a denial, an optional
reason. Approval does not wait for publishing to finish: read the request
until `data.status` is `fulfilled` or `fulfillment_failed`. If publishing
fails, `data.execution.message` explains what to do next.

Send the list item's `reviewEtag` or the detail response's complete opaque
`ETag` unchanged in `If-Match`. Reload after an ownership change and use the
new tag, even if no new request event was recorded.

A missing header returns 428 (`precondition_required`); a malformed or weak
value returns 400 (`invalid_request`); a tag made stale by a new request
event or a change to the visible Workspace returns 412
(`precondition_failed`); a current tag on a request that is no longer
pending returns 409 (`request_already_resolved`); and a decision that the
request cannot accept returns 409 (`conflict`).

A successful review returns 200 with the updated request, its ordered
public event history, a new ETag, and `Cache-Control: no-store, no-transform`.

A read made immediately after a review can briefly return the request's
previous state. If the response is lost, wait briefly and read the request
again. If it is resolved, do not retry; if still pending, retry only with
the latest read's ETag. The returned ETag changes with the request's event
history or visible Workspace, but not with every change to other response
fields.

## Path parameters

- `requestId` string, uuid, required

## Headers

- `If-Match` string, required: Opaque token for this request. Copy the complete value unchanged; do not construct or parse it.

## Request body

- union
  - object
    - `outcome` 'approved', required
  - object
    - `outcome` 'denied', required
    - `reason` string: Public denial reason returned in the request event history. Do not include confidential internal notes.

## Response `200`

Reviewed publishing approval request.

- PublishingRequestResponse
  - `data` PublishingRequestWithEvents, required
    - `id` string, uuid, required
    - `status` 'pending' | 'approved' | 'fulfillment_started' | 'fulfilled' | 'fulfillment_failed' | 'denied' | 'canceled', required
    - `workspaceId` string, nullable, required: Current workspace in this Account, or null when the Project no longer belongs to this Account.
    - `projectId` string, uuid, required
    - `requesterUserId` string, required
    - `version` integer, required: Increments with every recorded event. Use the ETag header, not this field, for conditional requests. A change to the visible Workspace can change the ETag without a new event.
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required
    - `events` PublishingRequestEvent[], required
      - `sequence` integer, required
      - `eventType` 'request_started' | 'request_approved' | 'request_denied' | 'request_canceled' | 'request_fulfillment_started' | 'request_fulfilled' | 'request_fulfillment_failed', required
      - `fromStatus` 'pending' | 'approved' | 'fulfillment_started' | 'fulfilled' | 'fulfillment_failed' | 'denied' | 'canceled', required
      - `toStatus` 'pending' | 'approved' | 'fulfillment_started' | 'fulfilled' | 'fulfillment_failed' | 'denied' | 'canceled', required
      - `actor` union, required
        - object
          - `type` 'user', required
          - `userId` string, required
        - object
          - `type` 'api_key', required
          - `apiKeyId` string, uuid, required
        - object
          - `type` 'system', required
          - `systemId` string, required
      - `publicNote` string, nullable, required
      - `occurredAt` string, date-time, required
    - `execution` object, required: The publish started by approval.
      - `operationId` string, uuid, nullable, required: Identifier of the publish started by approval, or null until publishing starts.
      - `message` string, nullable, required: Explanation and next steps when publishing fails; otherwise null.

## Other responses

- `400`: The request parameters are invalid.
- `401`: The API key is missing, malformed, unknown, revoked, or expired.
- `403`: The API key lacks the required scope or Account entitlement.
- `404`: The requested resource does not exist or is not visible to this key.
- `409`: The request cannot accept the decision, or a required update is in progress.
- `412`: The If-Match precondition failed for the current request. Reload the request and retry.
- `428`: The request requires an If-Match header with the strong ETag from the latest read.
- `429`: The API key or operation exceeded its rate limit.
- `500`: The server encountered an unexpected error.
- `503`: A required API dependency is temporarily unavailable.

## Changes

- **2026-09-30** `6586aa4ce5a3`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/replit/apis/replit-api/changes/projects/publishing-requests/:requestId/reviews/post.md)

---

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