***

title: tap
slug: /reference/python/relay/call/tap
description: Intercept call media and stream it to an external destination.
max-toc-depth: 3
---------------------

For a complete index of all SignalWire documentation pages, fetch https://signalwire.com/docs/llms.txt

[tapaction]: /docs/server-sdks/reference/python/relay/actions

[calling-call-tap]: /docs/server-sdks/reference/python/relay/call#events

[call-events]: /docs/server-sdks/reference/python/relay/call#events

Intercept call media (audio) and stream it to an external destination such as a
WebSocket or RTP endpoint. Returns a
[`TapAction`][tapaction] that you can use to stop
the tap or wait for it to finish.

<Info>
  This method emits [`calling.call.tap`][calling-call-tap] events. See [Call Events][call-events] for payload details.
</Info>

## **Parameters**

<ParamField path="tap" type="dict" required={true} toc={true}>
  Tap configuration specifying which audio to intercept.
</ParamField>

<Indent>
  <ParamField path="tap.type" type="str" required={true} toc={true}>
    Tap type. Valid values: `"audio"`.
  </ParamField>

  <ParamField path="tap.params" type="dict" toc={true}>
    Tap parameters. Supports a `direction` key with the following values:

    * `"listen"` -- capture audio heard by the caller
    * `"speak"` -- capture audio spoken by the caller
    * `"both"` -- capture audio in both directions
  </ParamField>
</Indent>

<ParamField path="device" type="dict" required={true} toc={true}>
  Destination device for the tapped media.
</ParamField>

<Indent>
  <ParamField path="device.type" type="str" required={true} toc={true}>
    Device type.

    * `"ws"` -- WebSocket endpoint
    * `"rtp"` -- RTP endpoint
  </ParamField>

  <ParamField path="device.params" type="dict" required={true} toc={true}>
    Device-specific parameters (e.g., `uri` for WebSocket, `addr`/`port` for RTP).
  </ParamField>
</Indent>

<ParamField path="control_id" type="Optional[str]" toc={true}>
  Custom control ID. Auto-generated if not provided.
</ParamField>

<ParamField path="on_completed" type="Optional[Callable[[RelayEvent], Any]]" toc={true}>
  Callback invoked when the tap operation ends.
</ParamField>

## **Returns**

[`TapAction`][tapaction] -- An action handle with
`stop()` and `wait()` methods.

## **Example**

```python {16}
import asyncio
from signalwire.relay import RelayClient

client = RelayClient(
    project="your-project-id",
    token="your-api-token",
    host="your-space.signalwire.com",
    contexts=["default"],
)

@client.on_call
async def handle_call(call):
    await call.answer()

    # Tap audio and stream to a WebSocket endpoint
    action = await call.tap(
        tap={"type": "audio", "params": {"direction": "both"}},
        device={"type": "ws", "params": {"uri": "wss://example.com/tap"}},
    )

    # Tap runs in background; stop it later
    await asyncio.sleep(30)
    await action.stop()

client.run()
```