Some services you depend on also call you back with signed webhooks — PolicySign today;
Relay and lob-send have the same shape. To verify those webhooks your receiver needs the
provider's signing secret. Declare where you receive them and the builder provisions the secret
on every deploy. No copying whsec_… values into tawa config.
# Consumer — catalog-info.yaml (catalog 0.4.0+)
spec:
dependencies:
- service: policysign
webhooks:
path: /api/webhooks/policysign # where you receive PolicySign's webhooks
On deploy the pod gets POLICYSIGN_WEBHOOK_SECRET, the secret of your PolicySign webhook
endpoint for that URL. Verify with it as usual (Standard Webhooks).
No webhooks.path → nothing happens. A plain - service: policysign behaves exactly as before.
After dependency resolution, for each dependency with webhooks.path:
metadata.webhookProvider, published by the
provider's own deploy). No contract → a warning, nothing provisioned.{service}.tawa.pro,
{service}.sandbox.tawa.pro, …) — plus webhooks.path.https://{provider platform host}{provisionEndpoint} with the
builder's Bio-ID client_credentials token and
{ orgSlug: spec.owner, url, consumerService, environment }.secretEnv
(it lands in {service}-managed-secrets, never on the helm command line).| Environment | Receiver URL (no custom domain) | Endpoint |
|---|---|---|
| prod | https://my-app.tawa.pro/api/webhooks/policysign | its own |
| sandbox | https://my-app.sandbox.tawa.pro/api/webhooks/policysign | its own |
| uat | https://my-app.uat.tawa.pro/api/webhooks/policysign | its own |
Each environment has a different URL, therefore a different endpoint and a different secret.
The provider must be safe to call on every deploy, forever:
If secretEnv is already set on your service — tawa config set --secret or plain
tawa config set — the builder does not overwrite it:
| Your value vs the provider's | Build log |
|---|---|
| same | POLICYSIGN_WEBHOOK_SECRET: your tawa config value is kept; it matches … |
| different | [warn] … DIFFERS from the secret policysign signs … with + how to switch |
The comparison happens in memory; neither value is ever logged. To move to the automatic secret:
tawa config unset POLICYSIGN_WEBHOOK_SECRET
tawa deploy --prod
Provisioning never blocks a deploy. Each of these is a [warn] line and no injection (a manually
set value is still used):
secretEnv is a reserved platform variable or was already provisioned this deployWarnings link here.
Never to the build log, Koko, the build record, or the deploy snapshot — the manifest lists the
key with isSecret: true and no value.
Publish the contract in your catalog:
spec:
webhookProvider:
provisionEndpoint: /api/platform/webhook-endpoints # absolute path on your public host
secretEnv: POLICYSIGN_WEBHOOK_SECRET # must end _SECRET, _KEY, _TOKEN or _PASSWORD
Deploy once; the builder syncs it to Koko (metadata.webhookProvider). Remove it and redeploy to
withdraw it. An invalid contract is dropped with a build warning. secretEnv may not be a
reserved platform variable (BIO_CLIENT_SECRET, MONGODB_URI, …).
Your endpoint must:
| Requirement | Why |
|---|---|
POST, JSON body { orgSlug, url, consumerService, environment } | the builder's call |
respond { success: true, data: { endpointId, secret, match } } | match is shown in the build log |
authenticate the builder: verify the Bio-ID token by JWKS, require token_type: client_credentials and client_id on an allowlist (builder-prod) | an org check is not enough — every insureco service's token carries orgSlug: insureco |
| be idempotent: adopt, never rotate, never duplicate | it runs on every consumer deploy |
scope everything to orgSlug | never return another org's secret |
validate url against your SSRF guard | the URL becomes a delivery target |
audit with a hint only, and send Cache-Control: no-store | it returns a credential |
The builder reaches you on your public platform hostname — it runs off-cluster, so there is no
Janus in front and /internal/* conventions give no protection. Authenticate in the handler.
PolicySign's implementation: app/api/platform/webhook-endpoints/route.ts,
lib/services/webhookProvisioning.ts, lib/platformAuth.ts.
webhooks.path without the provider having deployed its contract — warns "publishes
no webhook provider contract". Deploy the provider first.DIFFERS warning appears and verification fails. Unset it.POLICYSIGN_WEBHOOK_SECRET with PolicySign's own config of the same
name — PolicySign's is the legacy platform-wide X-PolicySign-Signature key and is unrelated.The same idea for a secret that lives in the Locker rather than at a provider:
tawa config set --secret KEY --from-locker lk://insureco/<vault>/<item>, where the builder fetches the value itself.
See config.
Last updated: October 6, 2026