---
title: "Ban a member"
method: POST
path: "/v1/projects/{project}/members/{member}/ban"
tags: ["Members"]
---

# Ban a member

`POST /v1/projects/{project}/members/{member}/ban`

## Request

- Base URL: `https://api.subscriby.net`
- URL: `https://api.subscriby.net/v1/projects/{project}/members/{member}/ban`
- Auth: HTTP bearer

```bash
curl -X POST https://api.subscriby.net/v1/projects/$PROJECT_ID/members/$MEMBER_ID/ban \
  -H "Authorization: Bearer $SUBSCRIBY_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Repeated spam in the research channel."}'
```

Moves a `lead`, `customer`, `trialing` or `churned` member to `banned`, removes their access on every connector and emits `member.banned` with the optional `reason`. Answers `200` with the member; a member already banned is answered unchanged and emits nothing.

Connector-side eviction (kicks from the gated spaces, revoked invite links) runs asynchronously on the connector pipeline after the flip. The call returns as soon as Subscriby's state has been updated and the webhook event has been fired; the platform actions complete in the background.

## Path parameters

- `project` string, uuid, required
- `member` string, uuid, required

## Headers

- `Idempotency-Key` string, uuid, required

## Request body

- object
  - `reason` string: Why the member is banned; carried on the `member.banned` event and shown in the dashboard, never sent to the member.

## Response `200`

The member after the change.

- object
  - `data` MemberResource, required: A member: a project-scoped subscriber. Every payment is made on behalf of one, and every access grant resolves to one.
    - `id` string, required: The member's id within the project; the same person in another project is another member.
    - `project_id` string, required: The project the member belongs to.
    - `username` string, required: The member's handle on the connector they joined through, when the platform has one.
    - `first_name` string, required: The member's first name as the connector reported it.
    - `last_name` string, required: The member's last name as the connector reported it; null when the platform gave none.
    - `email` string, nullable: The address the member chose and verified themselves, their portal sign-in credential; null for most members, who join through a bot and are never asked. Present only for a token holding `project-user:view`; a roster-only token never sees the key.
    - `billing_email` string, nullable: Whatever address the member gave a payment provider at checkout, captured automatically on every new payment. Never verified and never used for authentication: treat it as identification only. Null for members who last paid before the capture existed or who joined by access code. Present only for a token holding `project-user:view`.
    - `identities` MemberIdentityResource[]: The member's connected connector accounts, embedded on the detail read and on a list narrowed with `identity`; absent elsewhere.
      - `id` string, required: The link's id, the value the disconnect endpoint takes.
      - `connector` string, required: The connector the account is on, by key.
      - `source` string, required: How the account was connected: `bot` (through the project's bot), `portal` (from the portal's Account & Recovery screen), `handshake`, `adopted` or `backfill`.
      - `preferred` boolean, required: Whether the project reaches the member through this account first.
      - `notify` boolean, required: Whether the member receives the project's messages on this account.
      - `external_id` string, required: The platform's own id for the account.
      - `display_name` string, nullable, required: What the platform calls the person.
      - `username` string, nullable, required: The account's handle when the platform has one; null otherwise.
      - `linked_at` string, required: When the account was connected, ISO 8601.
    - `source` unknown[], nullable: Where the member came from: the Smart Link credited with them, the door and the campaign tags. Null for a member who arrived with no link and no tags; see the member source object.
      - unknown
    - `status` union, required: Where the member stands: `lead` (met the bot, never paid), `trialing`, `customer`, `churned` (no active access) or `banned`.
      - string
      - 'lead' | 'trialing' | 'customer' | 'churned' | 'banned'
    - `joined_at` string, nullable, required: When the member first joined the project, ISO 8601.
    - `created_at` string, nullable, required: When the member row was created, ISO 8601.
    - `updated_at` string, nullable, required: When the member row last changed, ISO 8601.

## Other responses

- `400`: `IDEMPOTENCY_KEY_MISSING`: every write needs an `Idempotency-Key` header. Send a fresh UUID per distinct operation.
- `401`: `AUTHENTICATION_REQUIRED`: the request carries no bearer token, or one that is revoked, malformed, or minted for another environment (an `sbt_test_` token on production).
- `403`: `TOKEN_MISSING_ABILITY`: the token is valid but does not carry the ability this endpoint requires; `error.context.required_ability` names the one to grant. An endpoint that also checks who owns a row or which tier the account is on answers `FORBIDDEN`, `TEAM_TIER_REQUIRED` or `CONNECTOR_TIER_REQUIRED` with the same status, and says so in its own description.
- `404`: `RESOURCE_NOT_FOUND`: an id in the path names nothing the token can see. `TENANT_MISMATCH`: the project sits outside the token's `scope:project:` allow-list, or the token carries no team scope. Both answer 404 rather than 403 so that existence outside the token's scope cannot be inferred.
- `409`: `IDEMPOTENCY_KEY_REUSED`: the key was already used in the last 24 hours with a different request body.
- `422`: `VALIDATION_FAILED`: the payload broke a rule, and `error.fields` maps each offending key to its messages. A refusal from the domain, such as a plan that cannot go on sale or a member who cannot be removed, uses the same code with `error.message` saying why and no `fields`.
- `425`: `IDEMPOTENCY_REPLAY_IN_PROGRESS`: the first request with this key is still running; retry in a few seconds and the original response is replayed.
- `429`: `RATE_LIMITED`: the token has spent its 300 requests a minute or 10,000 an hour; `Retry-After` says when the next one is accepted.

---

[API](https://skmtc.dev/memberpass/apis/subscriby-rest-api.md) · [All operations](https://skmtc.dev/memberpass/apis/subscriby-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/memberpass/apis/subscriby-rest-api/revisions/59c4df91d764?raw)
