---
title: "Approve or deny one member access request"
method: POST
path: "/members/access-requests/{requestId}/reviews"
tags: ["Approval requests"]
---

# Approve or deny one member access request

`POST /members/access-requests/{requestId}/reviews`

## Request

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

Approve or deny one pending member access request. The API key's creator must be allowed to change member roles in the workspace.

Approving promotes the requester from viewer to member when a seat is available and the workspace's membership policy allows it. A requester who is already a member or admin is fulfilled without using another seat. If the seat cannot be granted, for example because no seat is available or the workspace's roles are managed by an identity provider, the request becomes `fulfillment_failed` and no role changes.

Send `If-Match` with the list item's `reviewEtag` or the detail response's
complete strong `ETag`, unchanged.
The tag matches as long as no new event has been recorded on the request
since that read; changes to the requester's profile details alone do not
make it stale. A missing header returns 428
(`precondition_required`); a malformed or weak value returns 400
(`invalid_request`); a stale version returns 412
(`precondition_failed`); and a current tag on a request that is no longer
pending returns 409 (`request_already_resolved`).

A successful review returns 200 with the updated request, its public
history, a new `ETag`, and `Cache-Control: no-store, no-transform`.
`requester` is included only when the key also has `read:*`, and remains
null when requester details are unavailable. The ETag can differ from a
subsequent detail GET's ETag at the same request version.

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 it is still pending, retry only
with the latest read's ETag.

## Path parameters

- `requestId` string, uuid, required

## Headers

- `If-Match` string, required

## Request body

- union: The review outcome.
  - object
    - `outcome` 'approved', required
  - object
    - `outcome` 'denied', required
    - `reason` string: Denial reason recorded on the request's review event and returned in its event history. Must not contain NUL characters.

## Response `200`

The updated request, its public history, and its new ETag. The requester is included only when the key also has `read:*`; a write-only key receives `requester: null`.

- MemberAccessRequestResponse
  - `data` object, required
    - `id` string, uuid, required: Stable identifier for this request.
    - `type` 'member_seat_upgrade', required: The access change requested. Today only a viewer asking for a member seat.
    - `status` 'pending' | 'notified' | 'approved' | 'fulfillment_started' | 'fulfilled' | 'fulfillment_failed' | 'denied' | 'canceled', required: Where the request is in its lifecycle. - `pending`: waiting for an admin to approve or deny it. - `approved`: an admin approved it and the seat change is being applied. - `fulfillment_started`: the seat change is in progress. - `fulfilled`: the requester now has a member seat. - `fulfillment_failed`: an admin approved it, but the seat could not be granted, for example because no seat was available or the workspace's roles are managed by an identity provider. No role changed. - `denied`: an admin denied it. - `canceled`: the request was withdrawn. - `notified`: the request was handed to the Account's identity provider, which manages roles for this workspace. Replit does not review these requests.
    - `workspaceId` string, required: Workspace the requester wants a member seat in.
    - `requesterUserId` string, required: Account user who asked for the seat.
    - `requester` ApprovalRequester, required
      - `id` string, required
      - `username` string, required
      - `email` string, email, required
      - `firstName` string, nullable, required
      - `lastName` string, nullable, required
    - `reason` string, nullable, required: The requester's message. Null when the request was recorded without one.
    - `alreadyMember` boolean, nullable, required: True when the requester already held a member seat at approval time, so approval changed nothing. Null until the request is fulfilled.
    - `version` integer, required: Increments with every recorded event. Use the ETag header, not this field, for conditional requests.
    - `createdAt` string, date-time, required
    - `updatedAt` string, date-time, required
    - `events` MemberAccessRequestEvent[], required: Ordered public history, ascending by sequence.
      - `sequence` integer, required: Position in the request history. Sequence 1 is the creation event.
      - `eventType` 'request_started' | 'request_approved' | 'request_denied' | 'request_canceled' | 'request_fulfillment_started' | 'request_fulfilled' | 'request_fulfillment_failed', required
      - `fromStatus` 'pending' | 'notified' | 'approved' | 'fulfillment_started' | 'fulfilled' | 'fulfillment_failed' | 'denied' | 'canceled', required: Where the request is in its lifecycle. - `pending`: waiting for an admin to approve or deny it. - `approved`: an admin approved it and the seat change is being applied. - `fulfillment_started`: the seat change is in progress. - `fulfilled`: the requester now has a member seat. - `fulfillment_failed`: an admin approved it, but the seat could not be granted, for example because no seat was available or the workspace's roles are managed by an identity provider. No role changed. - `denied`: an admin denied it. - `canceled`: the request was withdrawn. - `notified`: the request was handed to the Account's identity provider, which manages roles for this workspace. Replit does not review these requests.
      - `toStatus` 'pending' | 'notified' | 'approved' | 'fulfillment_started' | 'fulfilled' | 'fulfillment_failed' | 'denied' | 'canceled', required: Where the request is in its lifecycle. - `pending`: waiting for an admin to approve or deny it. - `approved`: an admin approved it and the seat change is being applied. - `fulfillment_started`: the seat change is in progress. - `fulfilled`: the requester now has a member seat. - `fulfillment_failed`: an admin approved it, but the seat could not be granted, for example because no seat was available or the workspace's roles are managed by an identity provider. No role changed. - `denied`: an admin denied it. - `canceled`: the request was withdrawn. - `notified`: the request was handed to the Account's identity provider, which manages roles for this workspace. Replit does not review these requests.
      - `actor` union, required: Who performed the action recorded by this event.
        - 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: Public note recorded with the event, such as a denial reason.
      - `occurredAt` string, date-time, required: When the event was recorded.

## 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 is no longer pending or the decision conflicts with its current state. Read the request again before retrying.
- `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-19** `ba193431df44`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/replit/apis/replit-api/changes/members/access-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)
