> For the complete documentation index, see [llms.txt](https://www.conserver.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.conserver.io/conserver/day-in-the-life-of-a-vcon.md).

# Day In the Life of a vCon

Follows one phone call through an adapter, a conserver chain, storage and the vCon MCP server, so you can see what each part adds to the record.

The easiest way to understand a conserver is to follow one conversation through it. This page does that. A customer calls a car dealership's service line, and we watch the record of that call travel from the phone system to an AI assistant answering a manager's question about it a week later, picking up a transcript, a summary, a tag, a CRM record and two signed receipts on the way.

The same trip is drawn as a conveyor belt in [The Journey of a vCon](/vcons/vcon-conveyor-infographic.md). The station names below match that picture, so you can read the two side by side.

For the example, assume a conserver is running with one chain configured for the service line. The chain records the vCon on a transparency ledger, transcribes it, summarizes it, checks whether the customer mentioned a safety recall, routes the ones that did to the service team, looks the caller up in the CRM, and stores the finished record in Postgres, S3 and the vCon MCP server. Every step but one is a configuration choice. The exception is a short CRM lookup the dealership wrote itself.

<figure><img src="https://3800246470-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCIFRJT7WEl06BGNeuqeo%2Fuploads%2Fgit-blob-37cc3d7bf1cdd1ba559d730402f29c0f43bdea5e%2Fvcon-day-stations.svg?alt=media" alt="The stations one vCon passes through, from the phone system to an AI assistant"><figcaption><p>One conversation, twelve stops. The adapter builds the vCon, the chain grows it, storage keeps it, the MCP server answers questions about it.</p></figcaption></figure>

## 0. The conversation

A customer calls the dealership about a noise from the front of her car. The service advisor answers. The call is recorded, and the recording announcement at the start of the call tells her so. Nothing about this is special yet. The phone system holds an audio file and some SIP signaling, in whatever format the phone system prefers, and if nothing else happened that is where the conversation would stay.

## 1. The adapter

The phone system forks the call to a SIPREC recording server, and a [SIPREC adapter](/tools/vcon-siprec-adapter.md) is listening there. When the call ends, the adapter builds the vCon.

It creates a `parties[]` entry for the customer and one for the advisor, each carrying the SIP address, contact and display name that the signaling revealed. It creates a `dialog[]` entry of type `recording` that points at the audio by URL and carries a SHA-512 hash of the file, so the recording can live in object storage while the vCon stays small. It files the raw INVITE, the SDP and the STIR/SHAKEN PASSporT as attachments, each marked with a `purpose` such as `sip-invite` or `stir-passport-extended`, and declares `sip-signaling` in `extensions[]`.

Then it adds the attachment that governs everything downstream: a `lawful_basis` record. The basis is `consent`, the proof mechanism is the recording announcement at `dialog_index` 0, the `purpose_grants[]` say what the dealership may do with this conversation (`recording`, `transcription` and `analysis`, each `granted: true`) and there is an expiration date. The permission now travels with the conversation instead of sitting in a policy document.

The adapter assigns the vCon a v4 UUID. That UUID is the conversation's name for the rest of its life. Adapters are source specific. Those published in vcon-dev include Twilio, SignalWire, SIPREC, Sippy, ElevenLabs, audio file drops and AI agent sessions, plus a template for writing your own. Each speaks one platform's dialect and emits the same container. See [vCon Adapters](/vcon-adapters.md).

## 2. Onto the belt

The adapter posts the vCon to the conserver:

```
POST /api/vcon?ingress_lists=service_calls
x-conserver-api-token: ...
```

The conserver stores the JSON in Redis under the key `vcon:{uuid}` and pushes the UUID onto the `service_calls` list, all in one call. An external partner holding a key scoped to that list would send the same vCon to `POST /vcon/external-ingress?ingress_list=service_calls` instead, and that key works for nothing else.

From here the vCon is never passed between processes. Every link reads it from Redis and writes it back to Redis, which matters when the dialog holds an hour of audio. The list is durable buffering. If ten thousand calls end at five o'clock, the list absorbs them and workers pull at their own pace. See [Integrating Your App](/conserver/integrating-your-app.md).

## 3. The chain

A conserver worker is already blocked on a Redis `BLPOP` across every ingress list, so it receives the UUID the moment it lands. The chain configured on that list is an ordered set of links, written in YAML:

```yaml
chains:
  service_calls:
    ingress_lists: [service_calls]
    links:
      - ledger_created
      - transcribe
      - summarize
      - recall_check
      - recall_route
      - crm_lookup
      - ledger_enhanced
      - expire
    storages: [postgres, s3, vcon_mcp]
    egress_lists: [service_done]
```

Each link takes the UUID, does one thing to the vCon, and returns the UUID to continue or nothing to stop. The exact rules are in [Concepts](/conserver/concepts.md#link). Because links take and return the same thing, order is a configuration choice and so is vendor. Change the YAML, `POST /config`, and every worker picks up the new chain on its next loop. No redeploy.

Before the first link runs, the chain's tracers fire once with a link index of -1 to record the starting state. A tracer watches. It never writes to the vCon. The one tracer that ships is JLINC, which signs a record of each transition, hashes the vCon as it stood, and files the result in an external archive. It fires again after every link that returns. See [Conserver Tracers](/conserver/conserver-tracers.md).

## 4. The first receipt

The first link is `scitt`, configured with `vcon_operation: vcon_created`. It signs a statement about the vCon with the conserver's key, registers it on a SCITT transparency service, and gets back a COSE receipt. With `store_receipt: true` the receipt is written into the vCon as an `analysis[]` entry of type `scitt_receipt`, carrying the ledger `entry_id`, the `cose_receipt` and the statement's subject.

That is the first entry in the conversation's lifecycle. Anyone holding the vCon later can prove it existed at this moment and has not been altered since, without trusting whoever stored it. See the [Lifecycle extension](/extensions/lifecycle.md).

## 5. Transcription

The second link is `transcribe`, configured with `vendor: deepgram`. It reads the recording from the dialog, skips it if the audio is shorter than `minimum_duration` (60 seconds by default), sends it to Deepgram (`nova-3` by default), and writes the result into `analysis[]`: the full text, timed segments and confidence scores, with `vendor` and `product` recorded so a reader knows what produced it.

Audio is opaque. Text is searchable, summarizable, redactable and auditable. This is the moment the conversation becomes data.

Swapping the vendor is an edit to the YAML: set `vendor` to `openai`, `groq`, `hugging_face` or `whisper_builtin` for a local model. `wtf_transcribe` is a separate link that writes the [WTF transcription format](/extensions/wtf-transcription.md) through the vfun service. If the vCon already carries a transcript the link notices and skips the work, which is what makes reprocessing a chain cheap.

## 6. Analysis and tagging

The third link is `analyze`, with a summary prompt. It reads the transcript out of `analysis[]`, sends it to the configured model, and writes a `summary` analysis back. Customer reports a grinding noise from the front wheels at low speed, about 30,000 miles, advisor booked an inspection for Thursday.

The fourth link is `check_and_tag`, configured with the `evaluation_question` "Does the customer mention a safety recall or a recall notice they received?", a `tag_name` of `recall` and a `tag_value` of `true`. The model reads the transcript, answers yes, since the customer said she had a recall letter in the glove box and wondered whether it was related, and the link writes the tag into the vCon's `tags` attachment. If the answer had been no, the link would have added nothing and the chain would have carried on.

This is the old "recall finder" from the first version of this page, done without writing a link. Every question the business wants asked of every call is one `check_and_tag` entry in the YAML.

## 7. Routing

The fifth link is `tag_router`, with a route that maps the `recall` tag to a Redis list called `recall_review` and `forward_original: true`. It pushes the UUID onto `recall_review` and returns it, so the main chain continues. The conversation is now on two belts at once.

A second chain reads `recall_review`. In this deployment it has one link, `webhook`, which POSTs the whole vCon to the service scheduler's intake endpoint so the recall gets checked while the car is in for the noise. It could as easily have been `post_analysis_to_slack` with a template built from the summary, or a `jq_link` filtering for a particular model year. A chain's egress list is another chain's ingress list, and that is how long processes are built out of short ones.

## 8. The custom link

The sixth link is the dealership's own. `crm_lookup` is forty lines of Python following the [custom link template](/conserver/creating-custom-links.md). It reads the customer's number from `parties[]`, asks the CRM what it knows, and writes the answer back as an attachment carrying the account number, the vehicle's VIN, the open repair order and the customer's assigned advisor, with the party index that says who it refers to.

Three moves: read an identifier from the vCon, ask the system that owns it, write what comes back into the vCon. The next link, the storage layer, and the manager's assistant a week from now all read the CRM answer out of the record rather than asking the CRM again. One lookup, many readers.

## 9. The second receipt and the cleanup

The seventh link is `scitt` again, this time with `vcon_operation: vcon_enhanced`. The vCon has grown by a transcript, a summary, a tag and a CRM record since the first receipt, and this signs the new state. Two receipts in `analysis[]` now bracket the chain's work.

The last link is `expire_vcon`, which sets a 24 hour TTL on the Redis key. The working copy in Redis is a cache, and this is how it is kept from growing without bound. The record itself is about to land somewhere permanent.

## 10. Storage

No link stopped the chain, so the conserver first pushes the UUID onto the `service_done` egress list for anything that wants to run next, then writes the vCon to every storage configured on the chain, in parallel by default. `postgres` writes a row to the `vcons` table with the full document in a JSONB column, the subject and the timestamps. `s3` writes the file to `{path}/2026/09/02/{uuid}.vcon` by creation date. `vcon_mcp` POSTs the vCon to a running vCon MCP server's REST API, which puts it in the Supabase database that AI assistants will read from.

Fifteen storage modules ship with the conserver, from Elasticsearch and Milvus to Microsoft Dataverse and a SCITT storage that registers the finished vCon on the ledger without writing the receipt back. One write, many destinations, each tuned to a different reader. See [Storage](/conserver/storage.md).

Had any link thrown an exception, the UUID would have gone to `DLQ:service_calls` instead, and the vCon's Redis copy would have been kept for seven days so `POST /dlq/reprocess` can send it back through. Had only the S3 write failed, the UUID would have gone to `DLQ:storage:s3` and `POST /dlq/storage/reprocess` would retry that one write. The full rules are in [Concepts](/conserver/concepts.md#dead-letter-queues).

## 11. The MCP server

The conserver's work is done. It processed the conversation once, on arrival, whether or not anyone ever asks about it. Asking is the [vCon MCP server](/mcp-server.md)'s job.

The MCP server is the read path over the same store. It exposes 46 tools over the Model Context Protocol, so an AI assistant can search conversations by metadata, by keyword, by meaning, or by a hybrid of the two, then fetch one record or one component of one. When it is given a Redis cache it reads through it, so a conversation someone looked at a moment ago comes back quickly. The conserver hands each finished vCon to the MCP server's store through its `vcon_mcp` storage. The conserver decides what the record becomes. The MCP server decides what can be asked of it. See [Sharing a store with the conserver](/mcp-server/transport-and-deployment.md#sharing-a-store-with-the-conserver).

## 12. The assistant

A week later the service manager asks her assistant, in plain English, which customers mentioned a recall in the last seven days and whether each one has a repair order open.

The assistant calls `vcon_search` with the `recall` tag and a date range, gets a page of results with our vCon among them, and calls `vcon_fetch` on it asking for `summary`, `parties` and `attachments`. The summary tells it what the call was about. The CRM attachment tells it the repair order number and the advisor. The lawful basis attachment tells it that analysis was granted, so the answer it is about to give is one it was allowed to compute. It answers with the customer's name, the Thursday appointment, the open repair order and a citation back to the call.

Nobody built an integration for that question. The conversation was already a first class record, and the assistant read it the way it reads a file.

## Contents of the finished vCon

| Section         | What is in it                                                    | Who put it there             |
| --------------- | ---------------------------------------------------------------- | ---------------------------- |
| `parties[]`     | Customer and advisor, with SIP address, contact and display name | Adapter                      |
| `dialog[]`      | The recording, by URL, with a SHA-512 hash                       | Adapter                      |
| `attachments[]` | INVITE, SDP, STIR PASSporT (`purpose: sip-*`)                    | Adapter                      |
| `attachments[]` | `lawful_basis`: consent, purposes granted, proof, expiration     | Adapter                      |
| `analysis[]`    | `scitt_receipt` for `vcon_created`                               | `scitt` link                 |
| `analysis[]`    | Transcript with timed segments and confidences                   | `transcribe` link (Deepgram) |
| `analysis[]`    | Summary                                                          | `analyze` link               |
| `attachments[]` | `tags`: `recall: true`                                           | `check_and_tag` link         |
| `attachments[]` | CRM account, VIN, open repair order, advisor                     | Custom link                  |
| `analysis[]`    | `scitt_receipt` for `vcon_enhanced`                              | `scitt` link                 |
| `extensions[]`  | `sip-signaling`, `lawful_basis`, `lifecycle`                     | Adapter and chain            |

The record only grew. No link rewrote what an earlier one had written, which is what makes the account of the conversation part of the conversation. And the same JSON is in Postgres, in S3 and behind the MCP server, byte for byte, with two receipts that prove it.

## The deletion request

Some months later the customer sells the car and asks the dealership to delete her recordings. The dealership pushes the UUID through a one-link chain whose `scitt` link is configured with `vcon_operation: vcon_consent_revoked`, which registers the withdrawal on the transparency ledger. Then it calls `DELETE /vcon/{uuid}`. That removes the Redis copy and calls `delete` on every configured storage that implements it. Postgres, S3 and the vCon MCP storage all do, so the record is gone from all three. Storages without a delete (`mongo`, `sftp`, `milvus`, `dataverse`, `redis_storage`, `chatgpt_files`, `spaceandtime`, `scitt`, `webhook`) are skipped, so a deployment using them has to delete there by other means.

The conserver does not record `vcon_deleted` on the ledger by itself. Its `on_vcon_deleted` hook is a no-op that a deployment can replace at build time; this dealership's replacement registers the event. With that in place the vCon is gone. The proof that it existed, that consent was given and later withdrawn, and that deletion happened, stays on the ledger forever. When a regulator asks what happened to that conversation, the answer comes from the ledger rather than from reconstructed logs.

## The point

Each station alone is useful. Together they turn a phone call that would have sat in a recording bucket nobody opened into a record the business and its AI can use, and can prove. The adapter ends vendor lock in. The queue absorbs bursts, and the dead letter queues keep failures for replay. Transcription turns audio into data. The chain compounds value one link at a time, inside your security boundary. Storage serves every reader. The MCP server carries the result to where decisions are now being made. The receipts and the lawful basis attachment mean every one of those readers can trust what they are looking at.

## Related pages

* [Concepts](/conserver/concepts.md) defines the primitives this page walks through and the exact chain rules.
* [Standard Links](/conserver/standard-links.md) is the reference for all 23 shipped links, including every option used above.
* [Creating Custom Links](/conserver/creating-custom-links.md) is how `crm_lookup` gets written.
* [Configuring the Conserver](/conserver/configuring-the-conserver.md) covers the chain YAML in full.
* [Lawful Basis](/extensions/lawful-basis.md) and [Lifecycle](/extensions/lifecycle.md) are the two extensions that make the record governable.
* [The Journey of a vCon](/vcons/vcon-conveyor-infographic.md) is this page as a picture.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://www.conserver.io/conserver/day-in-the-life-of-a-vcon.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
