## TL;DR
"Conversation already closed" fires when you send a reply-type call against a closed conversation instead of a state-changing call. Reopening is a state transition, not a message: call the open/state endpoint first, then send the reply as a separate call. Check state before acting and your agent stops tripping over closed conversations.

## The query

```text
intercom api "conversation already closed" on reopen
```

## Use this when

- Reopen attempts fail with "already closed"
- Agents cannot reply to closed conversations via API
- You are building Intercom conversation automations
- Closed conversations need follow-up messages

## Not for

- Working the Intercom inbox UI
- Assigning conversations
- Analyzing close reasons
- Conversation ratings

## Steps

### 1. Separate the state change from the message

Reopening takes two calls: first transition the conversation to open, then post the reply. Combining them into one reply call is what produces the error. Model every closed-conversation interaction as state-then-message.

Expected output: reopen flows using two distinct calls.

### 2. Read conversation state before acting

Fetch the conversation and check its open/closed state before deciding which calls to make. Blind reply attempts against unknown state are the whole bug class. State first, action second.

Expected output: state read on every automated conversation action.

### 3. Handle the already-open case gracefully

If the conversation is already open, skip the state call and just reply. Reopen logic that assumes closed will error in the other direction. Both branches must exist.

Expected output: idempotent reopen handling for either state.

### 4. Serialize state changes and messages

Do not fire the open call and the reply concurrently; the reply can land before the state change applies and fail. Await the state transition, confirm it, then send. Ordering is load-bearing.

Expected output: ordered, confirmed state-then-message sequences.

### 5. Log state transitions for audit

Record who or what reopened each conversation and when. Reopens are customer-visible events; unexplained reopens erode trust. The log also debugs "it reopened itself" reports.

Expected output: an auditable reopen trail.

## Template: the checklist

```text
Intercom reopen handling:
[ ] State transition separated from the reply message
[ ] Conversation state read before acting
[ ] Already-open branch handled without error
[ ] State change awaited and confirmed before reply
[ ] Reopen events logged with actor and time
```

## Variant phrasings

### reopen closed intercom conversation api

Steps 1 and 2. State change first, then message.

### reply to closed conversation fails

Steps 1 and 4. Two calls, ordered.

### conversation already closed error

Steps 2 and 3. Check state, handle both branches.

## Why it happens

Intercom models "closed" as a real state with real guards, not as a label. The reply endpoint enforces the guard because replying to a closed conversation would silently resurrect it without intent. The API is forcing you to be explicit about reopening, which is correct behavior wearing an error's clothes.

## Edge cases

- Conversations closed by the customer: reopening may be unwanted. Check close source before auto-reopening.
- Snoozed versus closed: distinct states with distinct transitions. Do not treat them the same.
- Bulk reopen jobs: state-check each conversation; bulk blind reopens recreate this error at scale.
- Webhook-driven reopens: a close webhook arriving after your reopen needs ordering logic, or the conversation flaps.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_n-cNOK7aLB8_nwHIRT1qrw
