From a2c18584561b96443dfa4bb8073c5a09871cdccc Mon Sep 17 00:00:00 2001 From: Jeff Hoekman Date: Wed, 16 Sep 2026 14:01:27 -0400 Subject: [PATCH 1/4] VAP15-53 docs: document webhook credential withholding VAPSEC-564/619/623 changed a documented contract: when a server URL arrives in the API request itself - a transient assistant, or assistantOverrides - Vapi no longer attaches the org's webhook credential, and credential fields in the payload arrive as [REDACTED]. The page still promised the old behavior, so a customer hitting this sees an unauthenticated webhook and no explanation, indistinguishable from a broken listener. Adds three things to fern/server-url/server-authentication.mdx: - A Quick start warning, so the "Vapi will now authenticate requests" promise carries its condition where a reader meets it rather than 300 lines later. - A "When credentials are not attached" section: which paths are caller-authored, the contrast that saved assistants, phone numbers and tools keep their credentials, and the [REDACTED] payload note. - "Confirming a withheld send in your logs", carrying what support needs: the literal log string, the suppressed high-volume message types, and the trap that a missing entry does not mean credentials were sent. The rollout qualifier is not optional polish. Without it the page states the withholding unconditionally while pre-cutoff orgs are still gated by ENABLE_WEBHOOK_CREDENTIAL_BINDING, so an unenrolled org could read "no X-Vapi-Secret header" and conclude its secret is not reaching a third-party URL when it still is - an inaccuracy in the unsafe direction. It deliberately does not invite enrollment requests; the ramp is a hand-maintained list with limited slots, mostly allocated. ENFORCEMENT_DATE_TBD is a literal placeholder and a merge blocker. The VAPSEC-689 cutoff is still being settled with the CISO, infosec and the EM. A non-rendering MDX comment marks the spot. Scoped to the webhook server URL. The message-options override paths (tools, KB, custom voice, handoff and transfer destinations, endpointing) are VAPSEC-627 and are documented separately in VAP15-54. fern check: 0 errors, 12 pre-existing discriminator warnings. Refs VAP15-53, VAPSEC-564, VAPSEC-619, VAPSEC-623, VAPSEC-689 Co-Authored-By: Claude Opus 5 --- fern/server-url/server-authentication.mdx | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/fern/server-url/server-authentication.mdx b/fern/server-url/server-authentication.mdx index 0c4f77d18..797288f65 100644 --- a/fern/server-url/server-authentication.mdx +++ b/fern/server-url/server-authentication.mdx @@ -57,6 +57,10 @@ This replaces the previous inline authentication approach and provides better se + +Credentials configured for your org are attached only when the server URL also comes from your org configuration (a saved assistant, phone number, tool, or your org's server settings). When the server URL is included in the API request itself, then Vapi will send the webhook **without** your org's configured credential. See [When credentials are not attached](#when-credentials-are-not-attached). + + ## Creating Custom Credentials ### Dashboard Management @@ -380,6 +384,46 @@ const tool = await client.tools.create({ ``` +### When credentials are not attached + +Vapi attaches your org's credential only to webhook server URLs **in your org configuration**. A server URL supplied in the API request itself has the webhook sent to it without your configured org credential (no `credentialId` lookup, no `X-Vapi-Secret` header, no `Authorization` header). + +This applies when the API request server URL comes from: + +- a transient `assistant` passed inline to `POST /call`, `POST /chat`, or `POST /session` +- `assistantOverrides` on a call, chat, or session + +It does **not** apply to a saved assistant, phone number, or tool, or to your org's Server Configuration - those keep their credentials. + +{/* TODO(VAP15-53): replace ENFORCEMENT_DATE_TBD with the VAPSEC-689 cutoff date + (WEBHOOK_CREDENTIAL_BINDING_CUTOFF) once CISO / infosec / EM sign off. Do not merge + with the placeholder in place. */} + + +These rules apply to all new organizations - those created on or after ENFORCEMENT_DATE_TBD. To minimize disruption to legacy organizations created before that date, we are applying this change as a phased rollout. For legacy orgs, your org's credential will still be included with server URLs supplied in the request until the rollout reaches your org. + + + +If you supply the server URL in your API request, your endpoint receiving the webhook must enforce your own authentication. Either include the credential in your API request's `server.headers` parameters, or configure the URL on a saved resource instead, so that your org credential can be sent with it in the webhook. + + +Webhooks sent to a server URL supplied in the API request also have credential fields stripped from the payload. Any `apiKey`, `secret`, `password`, `authToken`, or similar field on the embedded `assistant`, `session`, or `chat` object arrives as the string `"[REDACTED]"` rather than its real value. The rest of the payload is unchanged. + +#### Confirming a withheld send in your logs + +When Vapi withholds credentials, it also writes an entry to the call's webhook logs: + +``` +Assistant credentials withheld from : this server url came from an +assistant this request supplied, not from your org configuration +``` + +The entry is written **once per message type, not once per send**. It is suppressed for the high-volume message types - `model-output`, `speech-update`, `status-update`, `transcript`, `transcript[transcriptType="final"]`, `voice-input`, and `voice-request`. + + +Absence of the webhook call log entry on a suppressed type does **not** mean credentials were sent. Credential withholding always happens on this path; only the log entry is suppressed. Check `end-of-call-report`, `call.artifact.upload`, or the session and chat webhooks - at least one of these is logged on every affected call. + + ## Credential Management ### Dashboard Features From dbf02a08ef32c4cdd1054764fb00a4ed53c250dc Mon Sep 17 00:00:00 2001 From: Jeff Hoekman Date: Wed, 16 Sep 2026 15:01:18 -0400 Subject: [PATCH 2/4] VAP15-53 docs: set the enforcement cutoff to 2026-09-23 Replaces the ENFORCEMENT_DATE_TBD placeholder and drops the TODO that guarded it. Unblocks merge. VAPSEC-689 deploys on Monday 2026-09-21. The date is stated in UTC because 2026-09-23T00:00:00Z is Tuesday 5pm Pacific: a cutoff written as "2026-09-22" would have landed Monday 5pm Pacific, the same day as the deploy and possibly before it, which trips the cutoff-ahead-of- BUILD_DATE invariant that PR #19995 machine-checks. Wednesday UTC leaves roughly thirty hours of margin and survives a Monday slip. The (UTC) marker is there because the boundary is an instant, not a calendar day. An org created Tuesday 5pm Pacific is created on 2026-09-23 UTC and is enforced, while its owner believes they signed up on the 22nd. This date must stay in sync with WEBHOOK_CREDENTIAL_BINDING_CUTOFF. Fern cannot interpolate it, so a cutoff change needs a companion docs PR. Recorded on VAPSEC-689. Refs VAP15-53, VAPSEC-689 Co-Authored-By: Claude Opus 5 --- fern/server-url/server-authentication.mdx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fern/server-url/server-authentication.mdx b/fern/server-url/server-authentication.mdx index 797288f65..685998656 100644 --- a/fern/server-url/server-authentication.mdx +++ b/fern/server-url/server-authentication.mdx @@ -395,12 +395,8 @@ This applies when the API request server URL comes from: It does **not** apply to a saved assistant, phone number, or tool, or to your org's Server Configuration - those keep their credentials. -{/* TODO(VAP15-53): replace ENFORCEMENT_DATE_TBD with the VAPSEC-689 cutoff date - (WEBHOOK_CREDENTIAL_BINDING_CUTOFF) once CISO / infosec / EM sign off. Do not merge - with the placeholder in place. */} - -These rules apply to all new organizations - those created on or after ENFORCEMENT_DATE_TBD. To minimize disruption to legacy organizations created before that date, we are applying this change as a phased rollout. For legacy orgs, your org's credential will still be included with server URLs supplied in the request until the rollout reaches your org. +These rules apply to all new organizations - those created on or after 2026-09-23 (UTC). To minimize disruption to legacy organizations created before that date, we are applying this change as a phased rollout. For legacy orgs, your org's credential will still be included with server URLs supplied in the request until the rollout reaches your org. From 55467b655643df4af0530619a937aefccf2a51cd Mon Sep 17 00:00:00 2001 From: Jeff Hoekman Date: Wed, 16 Sep 2026 15:08:18 -0400 Subject: [PATCH 3/4] VAP15-53 docs: apply Steve's style review Voice and terminology pass. Vapi becomes the actor throughout, spaced hyphens become periods or colons, the log fence is tagged text, and the suppressed-entry warning leads with what the reader actually has in front of them. "Server Configuration" now matches the dashboard label used in Quick start, and server.headers is called a field rather than a parameter. Two of the suggestions are applied with changes. The redaction sentence keeps its explicit scope. Steve's version opened "Vapi also strips credential fields", which drops the condition; the condition is the whole point, since payloads on org-configured URLs are not stripped, and a reader arriving by deep link has no surrounding context to infer it from. Takes the active voice without the cut. The rollout callout stays a Warning rather than becoming a Note. The concern behind the suggestion is real - four stacked Warnings train readers to skip them - but for an org that the phased rollout has not reached, this paragraph is the harm notice: the org credential is still going out to caller-supplied URLs today. Its terminology cleanup is applied, so one term covers what was four. Refs VAP15-53 Co-Authored-By: Claude Opus 5 --- fern/server-url/server-authentication.mdx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fern/server-url/server-authentication.mdx b/fern/server-url/server-authentication.mdx index 685998656..807345f2d 100644 --- a/fern/server-url/server-authentication.mdx +++ b/fern/server-url/server-authentication.mdx @@ -58,7 +58,7 @@ This replaces the previous inline authentication approach and provides better se -Credentials configured for your org are attached only when the server URL also comes from your org configuration (a saved assistant, phone number, tool, or your org's server settings). When the server URL is included in the API request itself, then Vapi will send the webhook **without** your org's configured credential. See [When credentials are not attached](#when-credentials-are-not-attached). +Vapi attaches your org's credential only when the server URL also comes from your org configuration: a saved assistant, phone number, tool, or **Server Configuration**. When the server URL comes from the API request itself, Vapi sends the webhook **without** that credential. See [When credentials are not attached](#when-credentials-are-not-attached). ## Creating Custom Credentials @@ -386,38 +386,38 @@ const tool = await client.tools.create({ ### When credentials are not attached -Vapi attaches your org's credential only to webhook server URLs **in your org configuration**. A server URL supplied in the API request itself has the webhook sent to it without your configured org credential (no `credentialId` lookup, no `X-Vapi-Secret` header, no `Authorization` header). +Vapi attaches your org's credential only to webhook server URLs **in your org configuration**. When the server URL comes from the API request itself, Vapi sends the webhook without your org credential: no `credentialId` lookup, no `X-Vapi-Secret` header, no `Authorization` header. -This applies when the API request server URL comes from: +This applies when the server URL in the API request comes from: - a transient `assistant` passed inline to `POST /call`, `POST /chat`, or `POST /session` - `assistantOverrides` on a call, chat, or session -It does **not** apply to a saved assistant, phone number, or tool, or to your org's Server Configuration - those keep their credentials. +It does **not** apply to a saved assistant, phone number, tool, or your org's **Server Configuration**. Those keep their credentials. -These rules apply to all new organizations - those created on or after 2026-09-23 (UTC). To minimize disruption to legacy organizations created before that date, we are applying this change as a phased rollout. For legacy orgs, your org's credential will still be included with server URLs supplied in the request until the rollout reaches your org. +These rules apply to organizations created on or after 2026-09-23 (UTC). Vapi is rolling the change out in phases to limit disruption to organizations created before that date. Until the rollout reaches your organization, Vapi still includes your credential with server URLs supplied in the request. -If you supply the server URL in your API request, your endpoint receiving the webhook must enforce your own authentication. Either include the credential in your API request's `server.headers` parameters, or configure the URL on a saved resource instead, so that your org credential can be sent with it in the webhook. +If you supply the server URL in your API request, your endpoint must authenticate the request itself. Either include the credential in the request's `server.headers` field, or configure the URL on a saved resource so Vapi can attach your org credential. -Webhooks sent to a server URL supplied in the API request also have credential fields stripped from the payload. Any `apiKey`, `secret`, `password`, `authToken`, or similar field on the embedded `assistant`, `session`, or `chat` object arrives as the string `"[REDACTED]"` rather than its real value. The rest of the payload is unchanged. +For webhooks sent to a server URL supplied in the API request, Vapi also strips credential fields from the payload. Any `apiKey`, `secret`, `password`, `authToken`, or similar field on the embedded `assistant`, `session`, or `chat` object arrives as the string `"[REDACTED]"` rather than its real value. The rest of the payload is unchanged. #### Confirming a withheld send in your logs When Vapi withholds credentials, it also writes an entry to the call's webhook logs: -``` +```text Assistant credentials withheld from : this server url came from an assistant this request supplied, not from your org configuration ``` -The entry is written **once per message type, not once per send**. It is suppressed for the high-volume message types - `model-output`, `speech-update`, `status-update`, `transcript`, `transcript[transcriptType="final"]`, `voice-input`, and `voice-request`. +The entry is written **once per message type, not once per send**. Vapi suppresses it for high-volume message types: `model-output`, `speech-update`, `status-update`, `transcript`, `transcript[transcriptType="final"]`, `voice-input`, and `voice-request`. -Absence of the webhook call log entry on a suppressed type does **not** mean credentials were sent. Credential withholding always happens on this path; only the log entry is suppressed. Check `end-of-call-report`, `call.artifact.upload`, or the session and chat webhooks - at least one of these is logged on every affected call. +A missing log entry on a suppressed type does **not** mean credentials were sent. Credential withholding always happens on this path; only the log entry is suppressed. Check `end-of-call-report`, `call.artifact.upload`, or the session and chat webhooks. At least one of these is logged on every affected call. ## Credential Management From a95979abcb6bbf5f8273be9dc14a0494b0674d2b Mon Sep 17 00:00:00 2001 From: Jeff Hoekman Date: Wed, 16 Sep 2026 15:22:45 -0400 Subject: [PATCH 4/4] VAP15-53 docs: demote the rollout callout to a Note Takes the last of Steve's suggestions. Four stacked Warnings in forty lines train readers to skip them, and this paragraph is the weakest candidate to keep as a Warning: its text is unchanged either way, and the whole callout is deleted under VAPSEC-654 once the flag is gone and the rollout no longer exists to qualify. Refs VAP15-53, VAPSEC-654 Co-Authored-By: Claude Opus 5 --- fern/server-url/server-authentication.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fern/server-url/server-authentication.mdx b/fern/server-url/server-authentication.mdx index 807345f2d..1b21996aa 100644 --- a/fern/server-url/server-authentication.mdx +++ b/fern/server-url/server-authentication.mdx @@ -395,9 +395,9 @@ This applies when the server URL in the API request comes from: It does **not** apply to a saved assistant, phone number, tool, or your org's **Server Configuration**. Those keep their credentials. - + These rules apply to organizations created on or after 2026-09-23 (UTC). Vapi is rolling the change out in phases to limit disruption to organizations created before that date. Until the rollout reaches your organization, Vapi still includes your credential with server URLs supplied in the request. - + If you supply the server URL in your API request, your endpoint must authenticate the request itself. Either include the credential in the request's `server.headers` field, or configure the URL on a saved resource so Vapi can attach your org credential.