getting Signature verification failed for webhook,...
# ask-ai
l
getting Signature verification failed for webhook, LIVEKIT_API_KEY, LIVEKIT_API_SECRET are same, it works if my agent worker is self hosted but when I am using Livekit Cloud Agent, it is failing, why is that .. how to resolve
t
Thanks for reaching out, ROHIT. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I see you're getting "Signature verification failed for webhook" only when using the LiveKit Cloud Agent even though your API key/secret are the same in your code. That works when self-hosted but fails in Cloud Agents. Here's why and how to fix it. Why this happens • LiveKit Cloud Agents automatically inject their own
LIVEKIT_API_KEY
,
LIVEKIT_API_SECRET
, and
LIVEKIT_URL
into the agent environment and ignore any user-supplied values for those variables. • Webhook signatures are created by the LiveKit server using your project-level API secret. The Cloud Agent’s injected secret is different, so verifying the incoming webhook with the injected
LIVEKIT_API_SECRET
causes a mismatch and the "Signature verification failed" error. • This mismatch doesn't occur for self-hosted agents because you control the environment and use the same secret for signing and verification. How to resolve it • Do not use the injected
LIVEKIT_API_SECRET
for webhook verification in Cloud Agents. • Ensure your WebhookReceiver uses your original project API secret (the secret that signs the webhooks), not
os.environ["LIVEKIT_API_SECRET"]
or the injected env var. Example (Python)
Copy code
from livekit import WebhookReceiver

# Use your project API secret directly (from secure config), not the injected LIVEKIT_* env var
receiver = WebhookReceiver(
    api_key="your-project-api-key",
    api_secret="your-project-api-secret"
)

event = receiver.receive(request_body, authorization_header)
Best practices • Use injected
LIVEKIT_*
env vars only for agent connection/authentication. • Store the project API secret separately (secure config, secrets manager, or bundled config) and pass it explicitly to the webhook verifier. • Avoid reading
LIVEKIT_API_SECRET
from the environment in Cloud Agent runs. If you want, tell me how you're currently loading secrets in your agent (env vars, secrets manager, config file) and I’ll suggest the exact change for your setup. Sources: Agents Framework | livekit/livekit_composite | DeepWiki | Agents (Python) | livekit/livekit_composite | DeepWiki
l
can we make agent use particular api keys so that the injected api keys are same as the token verifier api keys token_verifier = TokenVerifier( _api_key_=env.str("LIVEKIT_API_KEY"), _api_secret_=env.str("LIVEKIT_API_SECRET"), ) receiver = WebhookReceiver(token_verifier) webhook_event = receiver.receive(raw_body, auth_header)
t
Thanks — you're asking if the Cloud Agent can use specific API keys so the injected values match what your `TokenVerifier`/`WebhookReceiver` use. Short answer: you cannot make Cloud Agents use custom
LIVEKIT_API_KEY
/
LIVEKIT_API_SECRET
. Cloud Agents always inject their own credentials and ignore user-supplied values for those three env vars. What to do instead • Keep using the injected
LIVEKIT_*
vars for agent connection only. • Provide the project API key/secret you need for token/webhook verification separately (do not read
LIVEKIT_API_SECRET
from env). Pass that secret directly to
TokenVerifier
/
WebhookReceiver
from a secure config source. Example pattern (Python)
Copy code
# injected LIVEKIT_* used for agent auth (leave as-is)
# use a separate secure source for project secrets used to verify webhooks

project_api_key = get_secure("PROJECT_API_KEY")      # e.g., secrets manager or config file
project_api_secret = get_secure("PROJECT_API_SECRET")

token_verifier = TokenVerifier(
  api_key=project_api_key,
  api_secret=project_api_secret,
)

receiver = WebhookReceiver(token_verifier)
webhook_event = receiver.receive(raw_body, auth_header)
If you tell me how you currently store/load secrets in your Cloud Agent (env vars, secrets manager, config file), I can give the exact code snippet or deployment change to wire the project secret into your agent securely.
l
Are the injected keys visible in the livekit dashboard?
t
Short answer: No — the keys injected into Cloud Agents (
LIVEKIT_API_KEY
,
LIVEKIT_API_SECRET
,
LIVEKIT_URL
) are not visible in the LiveKit dashboard. Details: • Injected agent credentials are provided at runtime and are hidden from the agent’s secrets interface for security. • The original project API key and secret (the ones created in project settings) can be viewed in the project settings by project administrators. If those project secrets are lost, they must be regenerated. Sources: Server-side | livekit/livekit_composite | DeepWiki | server-sdk-go | livekit/livekit_composite | DeepWiki | Agents Framework | livekit/livekit_composite | DeepWiki | Mini tutorial: setting up a LiveKit Cloud project
l
Then how am I supposed to authenticate the webhook because the signatures will never match?
t
You're right to be concerned — the signatures will not match if you verify using the Cloud Agent's injected credentials. Here's how to authenticate webhooks correctly when using LiveKit Cloud Agents. Acknowledgement • You're asking how to verify webhook signatures when Cloud Agents inject different
LIVEKIT_*
credentials. What to do • Do not use the injected
LIVEKIT_API_SECRET
for webhook verification. • Retrieve your project API secret (the one that signs webhooks) from your project settings or your secure store. • Provide that project secret to your TokenVerifier/WebhookReceiver from a separate, non-injected source (secure env var, secrets manager, or config file). Example pattern (Python)
Copy code
# DO NOT read verification secret from LIVEKIT_API_SECRET when running in Cloud Agent
project_api_key = env.str("PROJECT_API_KEY")        # your project key stored separately
project_api_secret = env.str("PROJECT_API_SECRET")  # your project secret stored separately

token_verifier = TokenVerifier(
  api_key=project_api_key,
  api_secret=project_api_secret,
)

receiver = WebhookReceiver(token_verifier)
webhook_event = receiver.receive(raw_body, auth_header)
Best practices • Use injected
LIVEKIT_*
only for agent connection/authentication. • Store the project API secret in a secure place (secrets manager, encrypted config, or a protected env var like
PROJECT_API_SECRET
) and load it expressly for verification. • Avoid embedding the project secret in logs or exposing it in public code. If you tell me how you currently store secrets in the Cloud Agent (env vars, secrets manager, config file), I can give the exact code or deployment steps to wire
PROJECT_API_SECRET
into your agent securely.
l
This is exactly what I am doing
t
Thanks — I hear you: you’ve already implemented using a separate project secret for verification but it’s still failing. To help pinpoint why signatures still don't match, please share the following so I can diagnose precisely: 1. How you load secrets at runtime (choose one): ◦ reading directly from
os.environ["LIVEKIT_API_SECRET"]
◦ a
.env
file ◦
--secrets
or
--secrets-file
passed to
lk agent deploy
◦ a secrets manager (which one?) ◦ hardcoded in code/config file 1. The exact code snippet you use to construct
TokenVerifier
/
WebhookReceiver
(redact secrets). Example you showed earlier is helpful — paste the real lines you run. 2. How you deploy the Cloud Agent (CLI command or dashboard settings) and whether you include any secret overrides in that deployment. 3. Any agent logs around the failure (use
lk agent logs
) showing the "Signature verification failed" or related messages. Quick checks while you gather that: • Ensure no
.env
or deployment secret is unintentionally overriding
LIVEKIT_API_SECRET
. • Confirm your webhook handler actually reads the separate
PROJECT_API_SECRET
(not
LIVEKIT_API_SECRET
) when verifying. • Verify the webhook endpoint URL is the same one registered/used by the agent and is reachable publicly. Share the details above and I’ll point out the exact change to make or the log lines to look for. Sources: Server-side | livekit/livekit_composite | DeepWiki | server-sdk-ruby | livekit/livekit_composite | DeepWiki | examples/survey | livekit/livekit_composite | DeepWiki | Custom agent deployments | LiveKit docs | server-sdk-go | livekit/livekit_composite | DeepWiki
l
auth_header = request.headers.get("Authorization") if not auth_header: return Response( {"message": "Invalid authorization header"}, status=status.HTTP_400_BAD_REQUEST, ) token_verifier = TokenVerifier( _api_key_=env.str("LIVEKIT_API_KEY"), _api_secret_=env.str("LIVEKIT_API_SECRET"), ) # Get the exact raw body that was preserved by the parser raw_body = getattr(request, "_raw_body", None) if not raw_body: # This should not happen with the custom parser logger.error( "Raw body not found in request - parser may not be working" ) return Response( {"message": "Internal error - raw body not available"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) receiver = WebhookReceiver(token_verifier) webhook_event = receiver.receive(raw_body, auth_header) if not webhook_event: return Response( {"message": "Invalid webhook event"}, status=status.HTTP_400_BAD_REQUEST, ) I am selecting the .env file while lk agent deploy, the file has same api credentials as the place where webhook is authenticated lk agent logs is not showing any errors
t
Thanks — I can help. I see your webhook handler is reading
LIVEKIT_API_KEY
/
LIVEKIT_API_SECRET
from the env and you deploy with
lk agent deploy --secrets-file .env
. You said logs show no errors but verification still fails. Quick summary of likely cause • Cloud Agents auto-inject their own `LIVEKIT_API_KEY`/`LIVEKIT_API_SECRET` at runtime. Those injected values can differ from the project credentials that sign webhooks. • By passing a
.env
with the same names at deploy time you may be unintentionally conflicting with injected values or relying on the wrong source for verification. • If your handler reads
LIVEKIT_API_SECRET
expecting it to be the signing secret, but Cloud injects a different secret, signatures will fail. Immediate checks for me to diagnose Please answer these so I can pinpoint the issue: 1. Is the webhook endpoint running inside the Cloud Agent process, or on a separate backend service (outside the agent)? 2. Show the exact
lk agent deploy
command you run (redact secrets). 3. Confirm the contents of your
.env
file (redact values). Which variable names do you use for webhook verification? 4. Add and paste one debug log line you can run before verification: logger.info(f"ENV LIVEKIT_API_KEY={env.str('LIVEKIT_API_KEY', default='(missing)')} LIVEKIT_API_SECRET_present={ 'LIVEKIT_API_SECRET' in os.environ }") — paste the output. 1. Do you have any .env loading code (e.g., python-dotenv) that may run before the injected env is applied? Immediate fixes to try • Best: Move webhook verification to a separate backend service that loads the project signing secret from a dedicated variable (e.g.,
PROJECT_API_SECRET
) or a secrets manager. Do not rely on
LIVEKIT_API_SECRET
inside the agent for verification. • If webhook must run in the agent, stop reading
LIVEKIT_API_SECRET
for verification. Instead load the signing secret from a different name (e.g.,
PROJECT_API_SECRET
) stored in a source that is not overwritten by Cloud Agent injection. • Remove
LIVEKIT_API_KEY
/
LIVEKIT_API_SECRET
from the
.env
passed to
lk agent deploy
so there is no ambiguity about which secret is used at runtime. If you share the answers/log above I’ll give the exact code change or deploy command to fix this. Sources: Examples | livekit/livekit_composite | DeepWiki | Agents Framework | livekit/livekit_composite | DeepWiki | server-sdk-ruby | livekit/livekit_composite | DeepWiki | Agent Builder | LiveKit docs
l
I am using a separate backend service (outside the agent) for webhooks and building own dashboard I am indeed using dotenv in the agent code, how the signature will match if agent is injecting different credentials at runtime?
t
You're running the webhook handler in a separate backend — good. That means the Cloud Agent's injected credentials do NOT affect webhook verification in your backend. Why signatures will match • LiveKit signs webhooks with your project API secret. • Your separate backend should load and use the project API key/secret (e.g., via dotenv, a secrets manager, or env vars named something like
PROJECT_API_SECRET
) to verify the signature. • Because your backend uses the actual project secret that LiveKit used to sign the webhook, verification will succeed. The agent’s injected
LIVEKIT_*
values only matter inside the agent process and do not influence your external backend. What to check right now • In your webhook backend, ensure you load the project secret from a dedicated source (e.g.,
.env
or secrets manager) and pass that to `TokenVerifier`/`WebhookReceiver`. Do not use any env var value named
LIVEKIT_API_SECRET
if that name is also used inside Cloud Agent deployments. • Confirm the webhook endpoint URL points to your backend (not to the agent). • Log the key name used for verification (not the secret value) to confirm the backend is using
PROJECT_API_SECRET
or similar. If you share how you load the secret in the webhook service (dotenv snippet or config), I’ll give the exact minimal code change to ensure verification uses the project secret.
l
session_uid = self.ctx.room.name timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") req = api.RoomCompositeEgressRequest( _room_name_=session_uid, _audio_only_=True, _file_outputs_=[ api.EncodedFileOutput( _file_type_=api.EncodedFileType.OGG, filepath=_f_"recordings/{self.agent_name}/{session_uid}/{session_uid}_{timestamp}.ogg", # noqa: E501 azure=api.AzureBlobUpload( _account_name_=os.getenv("BLOB_STORAGE_ACCOUNT_NAME"), _account_key_=os.getenv("BLOB_STORAGE_ACCOUNT_KEY"), _container_name_=os.getenv( "BLOB_STORAGE_CONTAINER_NAME" ), ), ) ], webhooks=[ api.WebhookConfig( url=os.getenv("AGENT_CALL_RECORDING_WEBHOOK"), _signing_key_=os.getenv("LIVEKIT_API_KEY"), ) ], ) this is how I am signing
t
Thanks — I see the issue in the snippet: you set the webhook's
signing_key
to
os.getenv("LIVEKIT_API_KEY")
. That value can be the Cloud Agent–injected key (not your project key), so the webhook payload will be signed with the project key but the webhook config will advertise the injected key — causing verification mismatches in your backend. What to change • Use your project API key for the webhook
signing_key
(and ensure your backend verifies with the matching project secret). Do not use
LIVEKIT_API_KEY
which can be injected/overridden by the Cloud Agent runtime. Minimal corrected example
Copy code
api.WebhookConfig(
  url=os.getenv("AGENT_CALL_RECORDING_WEBHOOK"),
  signing_key=os.getenv("PROJECT_API_KEY"),   # use a dedicated project key variable
)
And in your webhook backend, verify with the project secret:
Copy code
token_verifier = TokenVerifier(
  api_key=os.getenv("PROJECT_API_KEY"),
  api_secret=os.getenv("PROJECT_API_SECRET"),
)
receiver = WebhookReceiver(token_verifier)
Best practices • Keep webhook signing credentials in dedicated env vars (e.g.,
PROJECT_API_KEY
/
PROJECT_API_SECRET
) or a secrets manager — do not reuse vars named
LIVEKIT_*
inside agents. • Ensure the webhook endpoint URL points to your external backend (which you said it does). • Avoid passing
LIVEKIT_API_KEY
/
LIVEKIT_API_SECRET
in the
.env
for agent deploy; let the Cloud Agent inject those for agent connections only. If you want, paste the names/contents of the env vars you use for deploy and for your backend (redact values) and I’ll confirm the exact variable names to use.