> ## Documentation Index
> Fetch the complete documentation index at: https://docs.webhook.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage provider secrets

> Register, list, and revoke the signing secrets webhook.co uses to verify inbound signatures — and rotate them with zero downtime.

A **provider secret** is the signing secret webhook.co uses to verify that an inbound webhook really came from your provider. You register one per endpoint per provider; from then on every matching event is checked and its **verification state** recorded. Secrets are sealed at rest, never returned by any read, and revoking one stops verification from honoring it immediately.

Adding and revoking need an API key with `endpoints:write`; listing needs `endpoints:read`.

## Add a secret

Give it the provider **slug** (one of 142 — see the [provider directory](/providers/directory)) and the secret value. Optionally attach a `label` to tell rotations apart. The CLI never takes the secret as an argument — it reads it from a hidden prompt or piped stdin, so it never lands in your shell history or the process list.

<CodeGroup>
  ```sh CLI theme={null}
  wbhk endpoints add-provider-secret <endpoint-id> --provider stripe --label "prod signing"
  # paste the secret at the hidden prompt, or pipe it non-interactively:
  printf %s "$STRIPE_SIGNING_SECRET" | \
    wbhk endpoints add-provider-secret <endpoint-id> --provider stripe --label "prod signing"
  ```

  ```sh API theme={null}
  curl -X POST https://api.webhook.co/v1/endpoints/<endpoint-id>/provider-secrets \
    -H "Authorization: Bearer $WEBHOOK_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"provider": "stripe", "secret": "whsec_your_signing_secret", "label": "prod signing"}'
  ```

  ```ts TypeScript theme={null}
  await webhook.providerSecrets.add({
    endpointId,
    provider: "stripe",
    secret: process.env.STRIPE_SIGNING_SECRET!,
    label: "prod signing",
  });
  ```

  ```python Python theme={null}
  client.provider_secrets.add(
      endpoint_id=endpoint_id,
      provider="stripe",
      secret=os.environ["STRIPE_SIGNING_SECRET"],
      label="prod signing",
  )
  ```
</CodeGroup>

Most providers sign with a single signing secret, which is the default. A couple need a second, differently-typed value — set `kind` to say which:

* `signing_secret` — the payload-signing secret the verify path uses. The default; omit `kind` for it.
* `verify_token` — a token echoed back to answer a `GET` verification handshake (for example a Meta `hub.verify_token`). It coexists with the signing secret under the same provider.
* `braintree_public_key` — Braintree's integration public key, needed to answer Braintree's challenge handshake. Registered over the API or SDKs.

An added secret is validated at the boundary against the same decoder the verify path uses, so a value that could never match is rejected up front rather than sitting there failing silently.

## List secrets

Listing returns **metadata only** — id, provider, label, status, and created-at. The sealed bytes and the plaintext are never in the response.

<CodeGroup>
  ```sh CLI theme={null}
  wbhk endpoints list-provider-secrets <endpoint-id>
  ```

  ```sh API theme={null}
  curl "https://api.webhook.co/v1/endpoints/<endpoint-id>/provider-secrets" \
    -H "Authorization: Bearer $WEBHOOK_API_KEY"
  ```

  ```ts TypeScript theme={null}
  const secrets = await webhook.providerSecrets.list(endpointId);
  for (const s of secrets) console.log(s.id, s.provider, s.status, s.label);
  ```
</CodeGroup>

Each secret's `status` is one of:

| Status     | Meaning                                                                  |
| ---------- | ------------------------------------------------------------------------ |
| `active`   | Honored by the verify path.                                              |
| `retiring` | Still honored — the state a superseded secret sits in during a rotation. |
| `revoked`  | No longer honored; kept as history.                                      |

## Revoke a secret

Revoking evicts the secret from the honored set and clears the ingest cache immediately, so verification stops trusting it right away. Pass the endpoint id and the secret id from the list.

<CodeGroup>
  ```sh CLI theme={null}
  wbhk endpoints revoke-provider-secret <endpoint-id> <secret-id>
  ```

  ```sh API theme={null}
  curl -X DELETE \
    "https://api.webhook.co/v1/endpoints/<endpoint-id>/provider-secrets/<secret-id>" \
    -H "Authorization: Bearer $WEBHOOK_API_KEY"
  ```

  ```ts TypeScript theme={null}
  await webhook.providerSecrets.revoke({ endpointId, secretId });
  ```
</CodeGroup>

<Warning>
  Revocation takes effect immediately — events signed only with the revoked secret start failing
  verification at once. During a rotation, add the new secret **before** you revoke the old one.
</Warning>

## Rotate without downtime

Providers roll signing secrets periodically. To rotate with no gap:

1. **Add** the provider's new secret. Both secrets are now honored — one `active`, the older one `retiring` — so events signed with either verify.
2. Wait for the provider to fully cut over to the new secret.
3. **Revoke** the old one.

No event fails verification in the window, because there's always at least one honored secret that matches.

Once a secret is registered, events arrive with a real verification state instead of `unattempted`. See [verify inbound signatures](/guides/verify-inbound-signatures) for what each state means.
