|
| 1 | +--- |
| 2 | +title: Redacting sensitive content |
| 3 | +sidebar-title: Content redaction |
| 4 | +slug: /ai/content-redaction |
| 5 | +author: Devon White |
| 6 | +x-custom: |
| 7 | + tags: |
| 8 | + - product:ai |
| 9 | + - product:voice |
| 10 | + - sdk:swml |
| 11 | +description: | |
| 12 | + Mask credit card numbers, PII, and other sensitive content from your |
| 13 | + AI agent's logs, events, webhooks, and call records — without changing |
| 14 | + the conversation itself. |
| 15 | +max-toc-depth: 3 |
| 16 | +--- |
| 17 | + |
| 18 | +[ai-params]: /docs/swml/reference/calling/ai/params |
| 19 | +[redact-prompt]: /docs/swml/reference/calling/ai/params#paramsredact_prompt |
| 20 | +[auto-correct]: /docs/swml/reference/calling/ai/params#paramsauto_correct |
| 21 | +[utility-model]: /docs/swml/reference/calling/ai/params#paramsutility_model |
| 22 | +[text-normalization]: /docs/swml/reference/calling/ai/params#text-normalization-values |
| 23 | +[prompt-engineering]: /docs/platform/ai/prompt-engineering |
| 24 | +[hipaa]: /docs/platform/compliance/hipaa |
| 25 | + |
| 26 | +AI agents routinely handle sensitive information — payment details, social security numbers, |
| 27 | +account credentials, names and addresses. Content redaction masks that information in |
| 28 | +everything SignalWire records or transmits about the call: logs, events, webhook payloads, |
| 29 | +the call timeline, and the post-conversation call log. |
| 30 | + |
| 31 | +The conversation itself is untouched. The caller hears the agent normally, and the agent |
| 32 | +understands the caller perfectly — only the records change. |
| 33 | + |
| 34 | +```mermaid |
| 35 | +%%{init: { |
| 36 | + 'theme': 'mc', |
| 37 | + 'themeVariables': { |
| 38 | + 'fontFamily': 'Inter', |
| 39 | + 'fontSize': '14px', |
| 40 | + 'nodeBorder': '2px', |
| 41 | + 'nodeTextColor': '#000000' |
| 42 | + }, |
| 43 | + 'flowchart': { |
| 44 | + 'wrappingWidth': 200 |
| 45 | + } |
| 46 | +}}%% |
| 47 | +flowchart TD |
| 48 | + classDef live fill:#f0fff0,stroke:#27ae60,stroke-width:2px,color:#000000,fontFamily:Inter |
| 49 | + classDef masked fill:#f0f8ff,stroke:#666,stroke-width:2px,color:#000000,fontFamily:Inter |
| 50 | + classDef note fill:#ffffd0,stroke:#666,stroke-width:1px,color:#666,font-style:italic,fontFamily:Inter |
| 51 | +
|
| 52 | + C["Caller: 'My card number is 4242 4242 4242 4242'"]:::live |
| 53 | + A["AI agent — receives the real number and replies normally"]:::live |
| 54 | + T["Caller hears: 'Thanks, your card has been updated.'"]:::live |
| 55 | + R["Logs · events · webhooks · timeline · call log |
| 56 | + 'My card number is ----'"]:::masked |
| 57 | +
|
| 58 | + C --> A |
| 59 | + A --> T |
| 60 | + A -.-> R |
| 61 | +
|
| 62 | + N1["The live conversation |
| 63 | + stays real"]:::note |
| 64 | + N2["Every record |
| 65 | + is masked"]:::note |
| 66 | + N1 -.-> A |
| 67 | + N2 -.-> R |
| 68 | +
|
| 69 | + linkStyle default stroke:#666666,stroke-width:2px,color:#666666 |
| 70 | +``` |
| 71 | + |
| 72 | +## Enable redaction |
| 73 | + |
| 74 | +Turn redaction on with a single parameter, [`redact_prompt`][redact-prompt], in the `ai` |
| 75 | +method's `params` block: |
| 76 | + |
| 77 | +<CodeBlocks> |
| 78 | +<CodeBlock title="YAML"> |
| 79 | +```yaml |
| 80 | +version: 1.0.0 |
| 81 | +sections: |
| 82 | + main: |
| 83 | + - answer: {} |
| 84 | + - ai: |
| 85 | + prompt: |
| 86 | + text: You are a payments support agent. Help callers update their billing details. |
| 87 | + params: |
| 88 | + redact_prompt: credit card numbers, CVVs, social security numbers, and full names |
| 89 | +``` |
| 90 | +</CodeBlock> |
| 91 | +<CodeBlock title="JSON"> |
| 92 | +```json |
| 93 | +{ |
| 94 | + "version": "1.0.0", |
| 95 | + "sections": { |
| 96 | + "main": [ |
| 97 | + { "answer": {} }, |
| 98 | + { |
| 99 | + "ai": { |
| 100 | + "prompt": { |
| 101 | + "text": "You are a payments support agent. Help callers update their billing details." |
| 102 | + }, |
| 103 | + "params": { |
| 104 | + "redact_prompt": "credit card numbers, CVVs, social security numbers, and full names" |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | + ] |
| 109 | + } |
| 110 | +} |
| 111 | +``` |
| 112 | +</CodeBlock> |
| 113 | +</CodeBlocks> |
| 114 | + |
| 115 | +The value of `redact_prompt` does two jobs: it switches redaction on, and it describes — in |
| 116 | +plain language — what counts as sensitive. Anything matching that description is replaced with |
| 117 | +`----` wherever the call is recorded or transmitted. |
| 118 | + |
| 119 | +<Warning> |
| 120 | +Redaction protects what the platform **records and transmits** — not what the AI processes. |
| 121 | +The agent still receives the caller's real words on every turn (that's what keeps the |
| 122 | +conversation working), and the caller always hears the agent's responses in full. If your |
| 123 | +requirement is to keep sensitive data from reaching the AI model itself, redaction does not |
| 124 | +do that. |
| 125 | +</Warning> |
| 126 | + |
| 127 | +## How it works |
| 128 | + |
| 129 | +Redaction covers both sides of the conversation. Your `redact_prompt` description guides the |
| 130 | +agent to treat matching content as sensitive whenever it speaks — the first time it says it, |
| 131 | +when it repeats it back, when it confirms it. What the caller says is masked separately, |
| 132 | +before it is stored or delivered. In both cases the real words still flow through the live |
| 133 | +conversation; only the records change. |
| 134 | + |
| 135 | +With redaction on, here is what each surface shows: |
| 136 | + |
| 137 | +| Surface | What appears | |
| 138 | +|---|---| |
| 139 | +| Audio the caller hears | The real content, spoken in full | |
| 140 | +| Text the AI model receives | The real content, every turn | |
| 141 | +| AI events and webhook payloads (including SWAIG and debug webhooks) | Masked — `----` | |
| 142 | +| Call timeline and stored conversation transcript | Masked — `----` | |
| 143 | +| Post-conversation `call_log` and `raw_call_log` | Masked — `----` | |
| 144 | + |
| 145 | +Redaction is performed by AI, not by a fixed pattern-matcher. It errs on the side of masking |
| 146 | +too much rather than too little, but it can occasionally miss — treat it as a strong safeguard |
| 147 | +for your logs and integrations rather than an absolute guarantee. |
| 148 | + |
| 149 | +## Keep it fast |
| 150 | + |
| 151 | +Redaction runs as a lightweight background task while the caller is waiting for a response. |
| 152 | +Two companion parameters keep that work quick. |
| 153 | + |
| 154 | +[`utility_model`][utility-model] selects the model used for background tasks like redaction |
| 155 | +and transcription cleanup. It defaults to the agent's main model, which is usually larger and |
| 156 | +slower than these tasks need. |
| 157 | + |
| 158 | +<Tip> |
| 159 | +Set `utility_model` to a small, fast model, such as `gpt-4o-mini`, `gpt-4.1-mini`, or |
| 160 | +`gpt-4.1-nano`, so background passes don't add noticeable latency to the agent's responses. |
| 161 | +</Tip> |
| 162 | + |
| 163 | +[`auto_correct`][auto-correct] cleans up the transcription of the caller's speech — |
| 164 | +converting spoken numbers to digits, formatting addresses and phone numbers, and fixing |
| 165 | +obvious mishearings. When used alongside `redact_prompt`, cleanup and redaction happen |
| 166 | +together in a single step instead of two. |
| 167 | + |
| 168 | +<Note> |
| 169 | +`auto_correct` only takes effect when [`enable_text_normalization`][text-normalization] — |
| 170 | +which is on by default — is set to `"off"`. The example below includes both settings. |
| 171 | +</Note> |
| 172 | + |
| 173 | +A complete configuration: |
| 174 | + |
| 175 | +<CodeBlocks> |
| 176 | +<CodeBlock title="YAML"> |
| 177 | +```yaml |
| 178 | +version: 1.0.0 |
| 179 | +sections: |
| 180 | + main: |
| 181 | + - answer: {} |
| 182 | + - ai: |
| 183 | + prompt: |
| 184 | + text: You are a payments support agent. Help callers update their billing details. |
| 185 | + params: |
| 186 | + redact_prompt: credit card numbers, CVVs, social security numbers, and full names |
| 187 | + utility_model: gpt-4o-mini |
| 188 | + auto_correct: true |
| 189 | + enable_text_normalization: "off" |
| 190 | +``` |
| 191 | +</CodeBlock> |
| 192 | +<CodeBlock title="JSON"> |
| 193 | +```json |
| 194 | +{ |
| 195 | + "version": "1.0.0", |
| 196 | + "sections": { |
| 197 | + "main": [ |
| 198 | + { "answer": {} }, |
| 199 | + { |
| 200 | + "ai": { |
| 201 | + "prompt": { |
| 202 | + "text": "You are a payments support agent. Help callers update their billing details." |
| 203 | + }, |
| 204 | + "params": { |
| 205 | + "redact_prompt": "credit card numbers, CVVs, social security numbers, and full names", |
| 206 | + "utility_model": "gpt-4o-mini", |
| 207 | + "auto_correct": true, |
| 208 | + "enable_text_normalization": "off" |
| 209 | + } |
| 210 | + } |
| 211 | + } |
| 212 | + ] |
| 213 | + } |
| 214 | +} |
| 215 | +``` |
| 216 | +</CodeBlock> |
| 217 | +</CodeBlocks> |
| 218 | + |
| 219 | +## Verify redaction |
| 220 | + |
| 221 | +<Steps> |
| 222 | + |
| 223 | +#### Place a test call |
| 224 | + |
| 225 | +Call your agent and read out a fake card number — for example, `4242 4242 4242 4242` — and |
| 226 | +ask the agent to repeat it back to confirm. |
| 227 | + |
| 228 | +#### Check the call records |
| 229 | + |
| 230 | +Open the call in your Dashboard and review the timeline and logs. Everywhere the number was |
| 231 | +said — by you or by the agent — should read `----`. |
| 232 | + |
| 233 | +#### Check your webhook payloads |
| 234 | + |
| 235 | +If your application receives SWAIG function calls, debug webhooks, or the post-conversation |
| 236 | +call log, confirm the sensitive values arrive masked there too. |
| 237 | + |
| 238 | +#### Sharpen the description if something leaks |
| 239 | + |
| 240 | +If one category keeps slipping through, name it explicitly in `redact_prompt` — for example, |
| 241 | +"including partial card numbers read back one digit at a time". |
| 242 | + |
| 243 | +</Steps> |
| 244 | + |
| 245 | +## Limitations |
| 246 | + |
| 247 | +- **It is not model-input privacy.** The AI model receives the real text every turn. |
| 248 | + Redaction keeps sensitive data out of your logs, webhooks, and call records — keeping it |
| 249 | + away from the model is a different requirement that redaction does not satisfy. |
| 250 | +- **It is best-effort.** Redaction is AI-driven and biased toward over-masking, but a value |
| 251 | + can occasionally slip through. Sharpen the `redact_prompt` description if a category leaks. |
| 252 | +- **It adds a small amount of background work per turn.** Point `utility_model` at a small, |
| 253 | + fast model, and enable `auto_correct` to fold cleanup and redaction into one step. |
| 254 | +- **It applies to text records, not audio.** The caller hears everything in full, and if you |
| 255 | + record calls, the recording still contains the real spoken audio. |
| 256 | + |
| 257 | +## Next steps |
| 258 | + |
| 259 | +<CardGroup cols={3}> |
| 260 | + <Card title="ai.params reference" href="/docs/swml/reference/calling/ai/params"> |
| 261 | + Full details for `redact_prompt`, `auto_correct`, and `utility_model`. |
| 262 | + </Card> |
| 263 | + <Card title="Prompt engineering" href="/docs/platform/ai/prompt-engineering"> |
| 264 | + Write prompts that keep your agent reliable in real conversations. |
| 265 | + </Card> |
| 266 | + <Card title="HIPAA compliance" href="/docs/platform/compliance/hipaa"> |
| 267 | + Build agents that handle protected health information. |
| 268 | + </Card> |
| 269 | +</CardGroup> |
0 commit comments