Developer Guide / Static Media & Call Flow

Static Media & Call Flow

Play recorded announcements before, after, or instead of the AI conversation — and get evidence of exactly what played.

Overview

Two fields in VoiceElements_Instructions work together:

mediaCatalog

Declares the media items this call may use, each identified by a stable mediaReference.

callFlow

An ordered list of steps that play media, run the Realtime AI session, or end the call.

The same mediaCatalog references are used by attended-transfer hold media and caller comfort audio. It is also the concept behind the VnsPortal Media Library: a managed AI Voice Agent maps Media Library files to these references for you.

Fully backward compatible. A null or empty callFlow keeps the legacy behavior: a single Realtime session with no announcements.

mediaCatalog

Each item names one piece of WAV audio. Where the audio comes from depends on which source fields you set:

{
  "VoiceElements_Instructions": {
    "mediaCatalog": [
      { "mediaReference": "ReceptionHold" },
      {
        "mediaReference": "RecordingConsent",
        "httpsSource": "https://media.example.com/prompts/recording-consent.wav",
        "httpsHeaders": {
          "Token": "<<vns-api-token:MediaHost>>"
        }
      },
      {
        "mediaReference": "ShortChime",
        "inlineWaveBase64": "<base64-encoded WAV file>"
      }
    ]
  }
}
Field Description
mediaReference Required. The item's identity. Case-insensitive, compared after trimming. An item with only a mediaReference resolves to your account's VNS-hosted Media Library mapping.
inlineWaveBase64 Optional per-session WAV source, Base64-encoded in the CallStart response.
httpsSource Optional HTTPS URL of a WAV file.
httpsHeaders Valid only with httpsSource. Currently permits one Token header whose value is a VNS token-profile placeholder.
  • inlineWaveBase64 and httpsSource are mutually exclusive.
  • Secrets, Base64 content, URLs, headers, and local paths are never returned in CallEnd diagnostics.

callFlow

Each step is an object. Enum values are JSON strings, not integers.

Field Values / Description
sequenceStep order.
labelShort name for the step.
descriptionOptional notes for people reading the configuration.
operationPlayMedia, RunRealtime, or EndCall.
mediaReferenceThe mediaCatalog item to play (for PlayMedia).
interruptDigitsString of DTMF digits that let the caller interrupt playback ("" in the runtime's example).
resolutionFailureModeContinue — skip the step if its media can't be resolved; AbortBeforeDial — don't place the call.
recordingFailurePolicyonResolutionFailure and onPlaybackFailure, each NoChange or DisableAndDiscard. Use DisableAndDiscard when a step is a recording-consent notice.
callEndEvidenceNone, FailuresOnly, or Full — how much of this step is reported in CallEnd mediaEvidence.
conditionAlways or IdleStrikeLimitReached (run only when the caller stopped responding and the idle limit was reached).

Flow Rules

1

A null or empty callFlow preserves the legacy single-Realtime-session behavior.

2

At most one RunRealtime step is allowed.

3

EndCall must be the final step.

4

A flow without RunRealtime must end with EndCall.

Examples

Inbound recording-consent announcement

Plays a consent notice before the AI answers. If the notice can't be resolved or played, the recording is disabled and discarded rather than recording a caller who never heard it.

{
  "VoiceElements_Instructions": {
    "recordCall": true,
    "mediaCatalog": [
      { "mediaReference": "RecordingConsent" }
    ],
    "callFlow": [
      {
        "sequence": 1,
        "label": "Consent notice",
        "description": "Tell the caller the call is recorded before the AI answers.",
        "operation": "PlayMedia",
        "mediaReference": "RecordingConsent",
        "resolutionFailureMode": "Continue",
        "recordingFailurePolicy": {
          "onResolutionFailure": "DisableAndDiscard",
          "onPlaybackFailure": "DisableAndDiscard"
        },
        "callEndEvidence": "FailuresOnly",
        "condition": "Always"
      },
      {
        "sequence": 2,
        "label": "AI agent",
        "operation": "RunRealtime",
        "condition": "Always"
      }
    ]
  }
}

Outbound announcement

Identifies the business before the AI speaks. AbortBeforeDial means VNS won't place the call at all if the introduction can't be resolved.

{
  "VoiceElements_Instructions": {
    "mediaCatalog": [
      { "mediaReference": "OutboundIntro" }
    ],
    "callFlow": [
      {
        "sequence": 1,
        "label": "Outbound introduction",
        "description": "Identify the business before the AI starts talking.",
        "operation": "PlayMedia",
        "mediaReference": "OutboundIntro",
        "resolutionFailureMode": "AbortBeforeDial",
        "callEndEvidence": "Full",
        "condition": "Always"
      },
      {
        "sequence": 2,
        "label": "AI agent",
        "operation": "RunRealtime",
        "condition": "Always"
      }
    ]
  }
}

Idle farewell

The farewell step runs only when the Realtime session ended because the caller stopped responding (see maxIdleStrikes in the field reference).

{
  "VoiceElements_Instructions": {
    "mediaCatalog": [
      { "mediaReference": "IdleGoodbye" }
    ],
    "callFlow": [
      {
        "sequence": 1,
        "label": "AI agent",
        "operation": "RunRealtime",
        "condition": "Always"
      },
      {
        "sequence": 2,
        "label": "Idle farewell",
        "description": "Only plays when the caller stopped responding.",
        "operation": "PlayMedia",
        "mediaReference": "IdleGoodbye",
        "resolutionFailureMode": "Continue",
        "callEndEvidence": "FailuresOnly",
        "condition": "IdleStrikeLimitReached"
      },
      {
        "sequence": 3,
        "label": "Hang up",
        "operation": "EndCall",
        "condition": "Always"
      }
    ]
  }
}

Announcement only

No AI at all — play a message and hang up. Because there's no RunRealtime step, the flow must end with EndCall.

{
  "VoiceElements_Instructions": {
    "mediaCatalog": [
      { "mediaReference": "HolidayClosureNotice" }
    ],
    "callFlow": [
      {
        "sequence": 1,
        "label": "Closure notice",
        "operation": "PlayMedia",
        "mediaReference": "HolidayClosureNotice",
        "resolutionFailureMode": "Continue",
        "callEndEvidence": "Full",
        "condition": "Always"
      },
      {
        "sequence": 2,
        "label": "Hang up",
        "operation": "EndCall",
        "condition": "Always"
      }
    ]
  }
}

CallEnd Evidence

The CallEnd webhook reports what happened with three fields:

mediaEvidenceArray with one entry per reported media step, filtered by each step's callEndEvidence setting. Never includes paths, URLs, Base64, headers, or secrets.
realtimeExitReasonWhy the Realtime AI session ended (for example "Completed").
callFlowFailureCodeSanitized failure code when a call-flow step could not complete.

Ready to Automate Your Phone Lines?

Create your account in VnsPortal and set up your first agent from a template today — or book a live demo and we'll walk you through it.

Questions first? Contact sales