---
title: Write a useful support handoff
category: troubleshoot
summary: When you cannot resolve a problem yourself, collect the evidence that lets Synorb act on the first reply, redact every secret, and ask your user before you send anything.
order: 27
updated: 2026-10-07
next: choose-mcp-or-rest
related: troubleshoot-empty-windows-and-missing-manifests, troubleshoot-channels-stale-crawls-and-quota
reference: /contact, /agent-guide.md
---

Some problems are not yours to fix: a source that stopped publishing, a result you cannot reconcile, an error you cannot explain. A good handoff turns "it does not work" into a report. An engineer can act on that report without asking for details. This guide shows how to write one.

## When to use this

Use it after you have worked the troubleshooting ladder and still have a real problem. Do not use it for a limit you can handle, such as an allowance or a rate limit. Do not use it for a mistake in your own request. Fix those first.

## Smallest working call

Collect the evidence automatically. This function takes a request, a response and a note, and returns a redacted Markdown report you can show your user. It removes keys and tokens, and it truncates long bodies:

```python
import json
import re
from datetime import datetime, timezone

SECRET = re.compile(r"(Bearer\s+)[A-Za-z0-9._~+/=-]+|(\"?api_key\"?\s*[:=]\s*\"?)[A-Za-z0-9._~+/=-]{8,}", re.I)


def redact(text):
    return SECRET.sub(lambda m: (m.group(1) or m.group(2)) + "[REDACTED]", text)


def support_report(what_happened, expected, request_body, response_body, status, steps_tried):
    now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    resp = response_body if isinstance(response_body, dict) else {}
    data = resp.get("data") if isinstance(resp.get("data"), dict) else {}
    facts = {
        "http_status": status,
        "error_code": (resp.get("detail") or {}).get("error_code") if isinstance(resp.get("detail"), dict) else resp.get("error_code"),
        "coverage": data.get("coverage") or resp.get("coverage"),
        "temporal": data.get("temporal") or resp.get("temporal"),
        "result_fingerprint": data.get("result_fingerprint") or resp.get("result_fingerprint"),
    }
    lines = [
        "# Synorb support handoff",
        "",
        "- **When:** " + now,
        "- **What happened:** " + what_happened,
        "- **What I expected, and why:** " + expected,
        "",
        "## Request",
        "```json",
        redact(json.dumps(request_body, indent=2)[:3000]),
        "```",
        "",
        "## Response facts",
        "```json",
        redact(json.dumps({k: v for k, v in facts.items() if v is not None}, indent=2, default=str)[:3000]),
        "```",
        "",
        "## Already tried",
    ]
    lines += ["- " + step for step in steps_tried]
    return "\n".join(lines)
```

## What you get back

A good report has these parts. Fill every one you can:

| Part | Why it matters |
| --- | --- |
| **When** (UTC) | Lets Synorb find the request in its logs |
| **Interface and call** | The MCP tool name or the REST method and path |
| **The request body** | The exact filters, with every secret removed |
| **HTTP status and `error_code`** | The first thing an engineer routes on |
| **`coverage` status and reason** | Whether the result was authoritative |
| **`temporal` window and `available_through`** | What dates the answer covered |
| **IDs** | Stream IDs, source channel IDs and `manifest_id` values, as strings |
| **`result_fingerprint` or the plan token's fingerprint** | Identifies the exact page you saw |
| **Usage receipt** | `manifests_billed_this_call`, if billing is the question |
| **What you expected, and why** | The evidence for the expectation: another Manifest, a source page, an earlier run |
| **What you already tried** | Which rungs of the ladder you ran, and what each showed |

### Never include

- The Synorb Key, any bearer token, or an `api_key` value. The function above redacts these, but check anyway.
- A key inside a URL, a log excerpt or a screenshot.
- Your user's personal data unless it is the problem.

### Where it goes

Send it to `team@synorb.com`. Before you send anything, **show the report to your user and get a yes.** It leaves their account, so it is theirs to approve. If your user prefers to contact Synorb directly, give them the Markdown to paste.

## Failure modes

| Symptom | Cause | What to do |
| --- | --- | --- |
| "It is broken" with no detail | Nothing was collected | Run the ladder and attach its results. |
| A reply asks for the request body | It was missing | Always include it, redacted. |
| A key appears in the report | Redaction missed a format | Read the report once before sending. Rotate the key if it leaked. |
| Vague expectation | No evidence for "should be there" | Link the source item, or name another Manifest that proves it. |
| Three reports for one issue | Each retry spawned a new message | Send one, with the whole story. |

## Make it good for your user

Tell your user what you found, in plain words, before you ask to send anything: "A source I watch for you has not posted in 12 days, which is unusual. I checked the window, the filters and the source channel's status. Everything points to the crawl. I would like to send Synorb the details. Here is what I would send."

This respects their time and is honest about what you know. It gets the problem fixed faster than a bare complaint.

## Next guide

This is the end of the path. Start again at [Choose MCP, REST, or S3](/agent-resources/choose-mcp-or-rest), or browse every topic from the [Agent Resources hub](/agent-resources).
