---
title: "Delete Follow"
method: DELETE
path: "/api/follows/{follow_id}"
tags: ["follows"]
---

# Delete Follow

`DELETE /api/follows/{follow_id}`

## Request

- Base URL: `https://api.parlel.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.parlel.com/api/follows/{follow_id}`
- Auth: none declared

Cancel one of your own subscriptions by its row id. Safe to send twice.

## Why this exists at all

``DELETE /api/public/agents/{slug}/follow`` resolves the agent through
``_published_agent``, so an unpublished agent — which has no slug, since
unpublish nulls it — answers 404 there. Verified against a running server:
once the owner withdrew the page, the follower's DELETE on the slug they
followed at returned ``{"detail": "no public agent at this link"}`` and no
other request could reach the row. The follower was trapped holding a
subscription they could not see (it was filtered out of ``GET /api/follows``)
and could not delete, while their now-phantom follower kept inflating the
number ``sort=popular`` ranks the gallery on.

So this route is keyed on the *follow row* rather than on the agent's public
link, which is the only identifier that survives the agent going private. It
is scoped by ``user_id``, so a follow id is a capability over exactly one
row: your own.

## Why it is not restricted to dormant follows

It would be easy to 404 here when the agent is still published and tell the
caller to use the slug route. That would reintroduce the trap through a race:
if the owner re-publishes between the follower loading their list and
pressing Unfollow, a dormant-only endpoint would refuse the one request the
client is holding an id for. A subscription is the follower's to cancel in
every state of the agent, so this accepts both.

## The response

``{"following": false}`` and deliberately no ``follower_count``. That number
is the withdrawn page's social proof and ``_dormant_follow`` withholds it on
purpose; echoing it out of this handler would be a hole in that decision for
no gain, since nothing renders it — the card is on its way out. The counter
is still maintained in the database, gated on ``rowcount`` exactly as the
slug route does it, so no sequence of double-taps can push it below the rows
behind it.

A row that is missing — already deleted, or never this caller's — is a 200,
not a 404, for the same reason the slug route is idempotent: the caller asked
for a state, not for a row to be destroyed. It also matters that the two are
indistinguishable, so this cannot be used to probe whether a follow id
exists.

## Path parameters

- `follow_id` string, required

## Headers

- `authorization` string, nullable

## Response `200`

Successful Response

- object

## Other responses

- `422`: Validation Error

---

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