> ## Documentation Index
> Fetch the complete documentation index at: https://developers.reintersect.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deferred answers

> Answer a command or message action after the delivery returns with defer(effect, { visibility }), handle failures in the deferred Effect, and answer from a worker.

> Reintersect apps are in developer preview. Names and limits can still change before general
> release.

Reintersect waits up to 30 seconds for a command or message action to answer. When the work takes
longer, `defer` answers the delivery at once and sends your answer when the work finishes.

## Defer with an Effect

`defer` is in the context of commands and message actions. Pass it an `Effect` that produces the
answer. It returns an `Effect` too, so yield it and return the result:

```tsx src/reportCommand.tsx theme={null}
import { Paragraph } from "@reintersect/sdk/components";
import { defineCommand } from "@reintersect/sdk/effect/app";
import { Effect } from "effect";
import { Reports } from "./reports";

export const ReportCommand = defineCommand(
  "report",
  { description: "Build this week's report" },
  function* ({ defer }) {
    const reports = yield* Reports;

    return yield* defer(
      Effect.map(reports.build, (report) => ({
        visibility: "public" as const,
        content: <Paragraph>{report.summary}</Paragraph>,
      })),
      { visibility: "public" },
    );
  },
);
```

The SDK answers the delivery with `202` and runs the deferred Effect after the response, with the
same services as the handler. When it succeeds, the SDK sends its answer with
[`POST /v0/invocations/respond`](/api-reference/invocations/respond). Services the deferred Effect
needs become requirements of the command, like anything else the handler yields.

Inside `Effect.map`, TypeScript widens `"public"` to `string`, so mark it `as const`, or annotate the
callback's return type as `InvocationAnswer` from `@reintersect/sdk/effect/app`.

## Who sees your app thinking

Until the answer arrives, Reintersect shows a row saying your app is thinking. The second argument
decides who sees that row:

| Call | Who sees the thinking row |
| - | - |
| `defer(effect)` | Only the member who ran the command or picked the action. |
| `defer(effect, { visibility: "public" })` | Everyone in the circle or direct message. |

The `202` body is always `{ visibility }`, carrying that choice. Showing the row to everyone is opt-in
because it shows which command or action ran, so use it when the answer will be public too.

The answer keeps its own `visibility`. A private answer after a public deferral still reaches only
the member who ran it, and the thinking row goes away for everyone else.

## When the deferred Effect fails

If the deferred Effect fails or dies, the SDK logs the cause and answers with a private `danger`
notice: "Couldn't finish this. Try again." The member sees the
failure card with **Run again**.

To answer a known failure in your own words, catch it inside the deferred Effect:

```tsx theme={null}
return yield* defer(
  reports.build.pipe(
    Effect.map((report) => ({
      visibility: "public" as const,
      content: <Paragraph>{report.summary}</Paragraph>,
    })),
    Effect.catchTag("ReportServiceDown", () =>
      Effect.succeed({ notice: { tone: "danger" as const, text: "The report service is down" } }),
    ),
  ),
  { visibility: "public" },
);
```

You have 15 minutes to answer, and a run takes one answer. Hosted runtimes keep deferred work alive
for a limited time after the response, so hand work that can take minutes to a queue.

## Answer from somewhere else

When a queue worker or another service does the work, defer without an Effect and pass the run's
`interactionToken` along:

```tsx src/reportCommand.tsx theme={null}
import { defineCommand } from "@reintersect/sdk/effect/app";
import { Schema } from "effect";
import { ReportQueue } from "./reportQueue";

export const ReportCommand = defineCommand(
  "report",
  {
    description: "Build a weekly report",
    options: Schema.Struct({
      week: Schema.String.annotations({ description: "The week, such as 2026-W42" }),
    }),
  },
  function* ({ defer, invocation, options }) {
    const queue = yield* ReportQueue;

    yield* queue.enqueue({ week: options.week, interactionToken: invocation.interactionToken });

    return yield* defer();
  },
);
```

`defer()` keeps the thinking row private; `defer(undefined, { visibility: "public" })` shows it to
everyone. Answer from the worker with the app client:

```ts src/finishReport.ts theme={null}
import { FetchHttpClient } from "@effect/platform";
import { ReintersectAppClient } from "@reintersect/sdk/effect";
import { Effect } from "effect";
import { app } from "./app";

interface FinishReportOptions {
  readonly installId: string;
  readonly interactionToken: string;
  readonly summary: string;
}

export const finishReport = ({ installId, interactionToken, summary }: FinishReportOptions) =>
  Effect.gen(function* () {
    const reintersect = yield* ReintersectAppClient.make(app, { installId });

    yield* reintersect.invocations.respond({
      interactionToken,
      answer: { visibility: "public", content: summary },
    });
  }).pipe(Effect.provide(FetchHttpClient.layer));
```

A second answer, or one after the 15 minutes, fails with `ReintersectAppInteractionExpiredError`.

## Testing

`runCommand` and `runMessageAction` return `{ status: "deferred", visibility, interactionToken }` for
a deferred run. Yield `host.settle` to let the deferred Effect finish, then read the answer:

```ts theme={null}
const outcome = yield* host.runCommand("report");

yield* host.settle;

const [response] = yield* host.pushesOf("invocations.respond");

expect(outcome).toMatchObject({ status: "deferred", visibility: "public" });
expect(response?.answer.visibility).toBe("public");
```

When the deferred Effect fails, the recorded answer is the SDK's failure notice. See
[Testing](/effect/testing).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.