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

# Experts and routing

> Define the expert registry, understand how ShoulderTap routes a request to the right person, and control escalation, rate limits, quiet hours, and opt-outs.

Experts are the humans ShoulderTap asks. You define them in `shouldertap.yaml`, and the engine tracks live state for each — whether they're muted, how many asks are outstanding, and how many they've had today. When a request arrives, the router picks the best-matched expert and the asker sends a well-formed message, subject to rate limits and quiet hours.

## The registry

Each expert has an id (their Slack user ID), a name, the topics they cover, and an optional escalation target.

```yaml theme={null}
experts:
  - id: U0123ABC
    name: Dana Kim
    topics: ["revenue metrics", "customer definitions"]
    escalation_to: U0456DEF
  - id: U0456DEF
    name: Marco Ruiz
    topics: ["revenue metrics"]

topics:
  "revenue metrics":
    fallback: U0456DEF
```

You can also read and replace the registry over HTTP with `GET /experts` and `PUT /experts` — but note that `PUT` rewrites the `experts:` section of `shouldertap.yaml`, which remains the source of truth. See the [Experts endpoint](/api/experts).

## How routing works

When a request arrives, ShoulderTap resolves an expert by trying these steps in order:

<Steps>
  <Step title="Target override">
    If the request names `target_experts`, routing is bypassed entirely and those experts are asked directly.
  </Step>

  <Step title="Exact topic match">
    The request's topic is normalized (lowercased, whitespace-collapsed) and compared against each expert's `topics`. Among exact matches, the request goes to the expert with the fewest **open asks** — a simple load balancer that spreads work across equally-qualified experts.
  </Step>

  <Step title="Fuzzy match">
    If no exact match exists, ShoulderTap falls back to token-overlap matching between the request topic and expert topics, accepting a match at 0.5 overlap or higher.
  </Step>

  <Step title="Topic fallback">
    If there's still no match, the request routes to the `fallback` expert configured for that topic.
  </Step>

  <Step title="No expert found">
    If nothing matches, the request fails with reason `no_expert_found`.
  </Step>
</Steps>

Muted experts and experts who are currently at their rate-limit cap are skipped during matching.

## The outbound ask

Every message the asker sends contains, in a fixed order that no code path can skip:

1. **Self-identification** — "I'm ShoulderTap, an automated assistant." It never pretends to be a person.
2. **Why it's asking** — the requesting consumer and the reason it's stuck (from the request's `context.asked_because`).
3. **The question** — one question, drafted by the LLM (if configured) to be answerable from memory. Without an LLM, the raw question text is sent verbatim.
4. **Effort framing** — "A one-or-two sentence reply here is all that's needed."
5. **Attribution promise** — the answer will be recorded with the expert's name as the source, after human review.
6. **Opt-out** — reply `mute` to stop receiving asks, or `skip` to pass this one along.

## Escalation

If an expert doesn't reply within `escalation_after` (default 2 hours), ShoulderTap re-asks their configured `escalation_to`. The request is marked `escalated`, and the original expert's open-ask count is released. If no one replies anywhere within `give_up_after` (default 24 hours), the request fails with reason `timeout` and the consumer is notified.

## Rate limits and quiet hours

ShoulderTap enforces these before every send — they are not merely advisory:

<CardGroup cols={2}>
  <Card title="Concurrent asks" icon="layer-group">
    `max_open_asks_per_expert` (default 3) caps how many unanswered asks an expert can hold at once. When all candidates are capped, the request is held rather than forced onto a busy expert.
  </Card>

  <Card title="Daily asks" icon="calendar-day">
    `max_asks_per_expert_per_day` (default 5) caps asks per expert per day. The count resets at the start of each day.
  </Card>

  <Card title="Quiet hours" icon="moon">
    `quiet_hours` (for example `["18:00", "09:00"]`, evaluated in `org.timezone`) suppresses DMs outside working hours. Asks are queued and sent when the window reopens.
  </Card>

  <Card title="Escalation targets" icon="arrow-up-right-dots">
    A capped or muted primary is skipped in favor of the next candidate, so a busy expert never blocks a request that someone else can take.
  </Card>
</CardGroup>

## Opt-outs: mute and skip

Experts control their own participation by replying to any ask:

* **`mute`** — a standing preference. The expert is marked muted and receives no further asks until unmuted. Routing skips muted experts.
* **`skip`** — a one-time pass. The current request is rerouted to the expert's escalation target (or dropped if there's none), and the expert's open-ask count for it is released. Unlike mute, skip does not change their standing availability.

<Note>
  A `mute` or `skip` reply that arrives after a proposal has already been decided is ignored — the answer is not affected, and skip does not reroute a request that's already resolved.
</Note>
