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

# Export conversations as signed IETF vCon containers

> Export any conversation as a portable, cryptographically signed IETF vCon envelope — parties, dialog, and metadata in one JSON artifact — and hand it to another AI system, carrier, or compliance archive with proof it was never altered.

# Export conversations as signed IETF vCon containers

The IETF vCon working group is standardising a container for conversation data: parties, dialog, attachments, analysis, and a signature, all in one JSON document. Orbit adopts that container as its canonical conversation export envelope. One API call turns any conversation in your workspace into a standards-conformant file you can hand to another AI system, a carrier, or a compliance archive — and the recipient can verify it was never altered.

Use the vCon export when a plain CSV or JSON dump is not enough:

* Hand a support thread to an external AI system that reads vCon directly, with the message order and party roles intact.
* Deliver a thread to a compliance archive as a tamper-evident artifact, not a spreadsheet anyone can edit.
* Prove to the recipient that the export came from your workspace — with your masking preferences already applied.

The [Conversation archive](/guides/conversation-archive) guide covers the CSV/JSON bulk export for filtered result sets. The vCon export is the per-conversation, signed counterpart; request it over the API.

## Why a signed container

A CSV row can be edited after the fact and no one can tell. A vCon is signed at export time, and the signature binds the exact bytes of the parties, dialog, attachments, and analysis sections. If a recipient changes so much as one character, the signature no longer verifies. The container also mints a fresh, tamper-evident `uuid` per export, so the artifact is addressable without exposing the internal conversation id.

## Export a conversation

Call `GET /conversations/:id/vcon` with an API key that has conversation read access (`conversations:read` — the same scope the inbox and archive use):

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/conversations/cnv_01HF3XAMPLE/vcon" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -o handover.vcon.json
```

The response is a JSON document with an `exported_at` date stamp and the signed container under `vcon`. Every export is recorded in your audit log, and the `Content-Disposition` header names the download `conversation-<id>-<date>.vcon.json`.

```json theme={null}
{
  "exported_at": "2026-08-24",
  "vcon": {
    "vcon": "0.3.0",
    "uuid": "8kPq3mZ1vQjR4sT7wY2x9A",
    "created_at": "2026-08-24T10:02:00.000Z",
    "subject": "Alice Example",
    "parties": [
      { "party": 0, "tel": "+14155552671", "name": "Alice Example", "role": "customer" },
      { "party": 1, "name": "support@yourcompany.com", "role": "agent" }
    ],
    "dialog": [
      {
        "type": "text",
        "start": "2026-08-24T10:00:00.000Z",
        "party": 0,
        "mimetype": "text/plain",
        "body": "My order never arrived.",
        "encoding": "none",
        "meta": { "channel": "whatsapp", "direction": "inbound", "status": "delivered" }
      },
      {
        "type": "recording",
        "start": "2026-08-24T10:05:12.000Z",
        "party": 1,
        "url": "https://files.orbit.devotel.io/m/att_01HF4SAMPLE",
        "meta": { "channel": "whatsapp", "direction": "outbound" }
      }
    ],
    "attachments": [
      {
        "type": "conversation_metadata",
        "encoding": "json",
        "body": {
          "channel": "whatsapp",
          "status": "resolved",
          "tags": ["refund"],
          "created_at": "2026-08-24T09:59:40.000Z",
          "closed_at": "2026-08-24T10:31:02.000Z"
        }
      }
    ],
    "analysis": [],
    "signatures": [
      {
        "protected": { "alg": "Orbit-HS256", "crit": ["alg"] },
        "payload_b64": "eyJ2Y29uIjoiMC4zLjAiLCJ1...",
        "signature": "tKb4LmQ7xR2vYp9..."
      }
    ]
  }
}
```

A `404` means no conversation with that id exists in your workspace. The export is read-only — it issues no outbound traffic of any kind.

## Container anatomy

| Field         | Contents                                                                                                                                                                                                                                                        |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `vcon`        | Container version. Orbit currently exports the `"0.3.0"` track of the IETF vCon container draft.                                                                                                                                                                |
| `uuid`        | A fresh 22-character base64url id minted per export. Public and tamper-evident — deliberately not the internal conversation id, so you can share artifacts without handing out workspace internals.                                                             |
| `created_at`  | RFC 3339 UTC timestamp of the export.                                                                                                                                                                                                                           |
| `subject`     | The conversation label: the contact name when known, otherwise a channel-and-id fallback such as `"whatsapp thread cnv_01HF3XAMPLE"`.                                                                                                                           |
| `parties`     | Party index 0 is the customer, carrying the `tel` and/or `mailto` address and display name when known. Index 1 is your side — the assigned agent or the generic "Orbit" system label. Dialog and analysis entries reference parties by this 0-based index.      |
| `dialog`      | One item per message, oldest first: `type: "text"` with `mimetype: "text/plain"` and the message body, plus one `type: "recording"` item per media attachment carrying its external `url`. Per-turn `meta` records the channel, direction, and delivery status. |
| `attachments` | Workspace metadata the recipient needs but the dialog does not carry: a `conversation_metadata` entry with the channel list, status, tags, assignee, and open/closed timestamps.                                                                                |
| `analysis`    | Reserved for caller-computed analysis (sentiment, summaries). Present and empty in exports today — the array is null-safe for receivers.                                                                                                                        |
| `signatures`  | One entry per signature. `protected` names the algorithm, `payload_b64` is the base64url of the signed payload, and `signature` is the HMAC over it. See the next section.                                                                                      |

## Verifying the signature

Orbit signs with a symmetric HMAC-SHA256 scheme labelled `Orbit-HS256`, using the same platform signing secret that protects your share links and CSAT tokens. This is an honest choice, not a shortcut: the platform's keyring holds an HMAC secret rather than an Ed25519 keypair, so a symmetric scheme is what the signature genuinely is. The consequence to plan around: verification requires the shared secret, so the verifier is a party you have shared that secret with — it is not public-key verification anyone can run.

The signed surface covers the `payload_b64` string itself, so stripping the signature from the file yields an invalid artifact rather than an "unsigned but valid" one. Verification also requires the signed payload's `uuid` to match the container's top-level `uuid`, which defeats a substitution attack that swaps in another validly-exported payload.

To verify by recompute:

```text theme={null}
given container, secret:
  sig := container.signatures[0]
  if sig.protected.alg != "Orbit-HS256": return false
  expected := base64url( HMAC-SHA256(key = secret, message = sig.payload_b64) )
  if expected != sig.signature: return false
  decoded := JSON.parse( base64urlDecode(sig.payload_b64) )
  return decoded.uuid == container.uuid
```

Recompute the HMAC over `payload_b64`, compare it with `signature`, and then confirm the decoded payload's `uuid` matches the container's top-level `uuid`. Reject anything that fails either check. The same symmetric verification is available inside the platform for re-import; it never throws, so a tampered artifact simply returns false.

## PII masking before signing

If you have turned on transcript PII redaction in your workspace privacy settings (`pii_redaction_in_transcripts`), the export applies the standard PII scan to every dialog body and to the party phone/email addresses before the signature is computed. The exported artifact never carries cleartext values you opted out of storing in transcripts.

Two consequences worth understanding:

* The signature binds the masked bytes, so a masked export verifies against what a recipient actually sees. There is no "cleartext version" of a masked export — masking is not a view.
* The flag fail-opens: if the setting cannot be read (for example a transient cache failure), the export proceeds unmasked, matching the default-off posture of the setting. If your archive requires masked exports, enable the setting before you export.

## Import and handoff

A vCon is a complete handoff artifact: parties identify whose thread it is, dialog preserves the message order sender-by-sender, attachments carry the operational context (channel, status, tags), and the signature lets the recipient confirm none of it drifted in transit.

Typical flows:

* **Compliance archive** — export on a schedule or per request, push the file into your archive, and let the archive recompute the HMAC against the secret you provisioned there. A file whose signature fails is quarantined, not ingested.
* **External AI system** — hand the container to a vCon-aware system for case migration or second-opinion analysis. Party roles and the oldest-first dialog survive the move; the per-turn `meta` gives the receiving system channel and direction without a second lookup.
* **Carrier handoff** — deliver the artifact with the shared secret out of band; the carrier verifies and can import the parties and dialog into their own tooling.

## Limits to plan for

* **One export per conversation.** The endpoint exports a single thread; for bulk filtered exports, use the [archive export](/guides/conversation-archive).
* **Two-party shape.** Party 0 is the customer, party 1 is your side. The `party_history` extension for multi-party crates (contact moves between participants) is not used by this export.
* **Per-export identifiers.** The `uuid` is minted per export and is not the internal conversation id; treat the pair (`uuid`, `created_at`) as the artifact's identity.
* **Transcript bound.** The dialog covers up to 500 messages per conversation, oldest first after export.
* **Version track.** Exports follow the `"0.3.0"` draft track of the IETF vCon container; expect the field to move forward as the draft matures.

## See also

* [Conversation archive](/guides/conversation-archive) — natural-language search plus bulk CSV/JSON export across channels
* [Search message history](/guides/search-message-history) — fielded single-message lookups by provider reference
* [Identity resolution](/guides/identity-resolution) — how Orbit resolves the party addresses the export carries
