Webhooks
Authpipe supports webhooks in two directions:
- Outbound — Authpipe sends events to your app (connection created, token refreshed, refresh failed, etc.)
- Inbound — External providers send webhooks to Authpipe, which verifies and forwards them
Outbound webhooks (Authpipe to your app)
Section titled “Outbound webhooks (Authpipe to your app)”Set up a webhook URL
Section titled “Set up a webhook URL”const result = await authpipe.setWebhookConfig("https://yourapp.com/webhooks/authpipe");
console.log(result.webhook_url); // "https://yourapp.com/webhooks/authpipe"console.log(result.secret); // "whsec_..." — save this securelyresult, err := client.SetWebhookConfig(ctx, &authpipe.SetWebhookConfigParams{ WebhookURL: "https://yourapp.com/webhooks/authpipe",})// result.Secret — save this securelyresult = client.set_webhook_config( webhook_url="https://yourapp.com/webhooks/authpipe",)# result.secret — save this securelyThe response includes a secret for signature verification. Store it securely — it’s only returned once.
Event types
Section titled “Event types”| Event | Trigger |
|---|---|
installation.created | App installed in external workspace |
installation.updated | Permissions or tokens changed |
installation.suspended | Installation paused by provider |
installation.deleted | Installation removed |
connection.created | User completes OAuth or credential stored |
connection.refreshed | Token silently refreshed |
connection.failed | Refresh or health check failed |
connection.revoked | User or admin disconnected |
connection.reauth_required | Token expired, needs user action |
connection.scopes_changed | Granted scopes modified |
connection.deleted | Connection permanently removed |
credential.stored | New credential encrypted and saved |
credential.rotated | Credential rotated (API key, webhook secret) |
webhook.received | Inbound provider webhook verified and processed |
Verify webhook signatures
Section titled “Verify webhook signatures”Authpipe signs outbound webhooks with HMAC-SHA256. Verify the signature using the secret from setWebhookConfig.
import crypto from "crypto";
function verifyWebhookSignature( payload: string, signature: string, secret: string,): boolean { const expected = crypto .createHmac("sha256", secret) .update(payload) .digest("hex"); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected), );}
// In your webhook handler:app.post("/webhooks/authpipe", (req, res) => { const signature = req.headers["x-authpipe-signature"]; if (!verifyWebhookSignature(req.rawBody, signature, WEBHOOK_SECRET)) { return res.status(401).send("Invalid signature"); }
const event = req.body; switch (event.event_type) { case "connection.created": console.log("New connection:", event.data); break; case "connection.failed": console.log("Connection needs attention:", event.data); break; } res.status(200).send("OK");});Rotate secrets
Section titled “Rotate secrets”Rotate the signing secret without downtime. After rotation, Authpipe supports both the old and new secrets during the transition.
const result = await authpipe.rotateWebhookSecret();console.log(result.secret); // new secret — update your appresult, err := client.RotateWebhookSecret(ctx)// result.Secret — the new signing secretresult = client.rotate_webhook_secret()# result.secret — the new signing secretAfter rotation:
secret_1= new secret (current)secret_2= old secret (still valid for verification)
Update your app to use the new secret, then both secrets will be valid until the next rotation.
Check webhook config
Section titled “Check webhook config”const config = await authpipe.getWebhookConfig();console.log(config.webhook_url); // your URLconsole.log(config.has_secret_1); // trueconsole.log(config.has_secret_2); // true (if rotated at least once)config, err := client.GetWebhookConfig(ctx)config = client.get_webhook_config()Delete webhook config
Section titled “Delete webhook config”await authpipe.deleteWebhookConfig();err := client.DeleteWebhookConfig(ctx)client.delete_webhook_config()Inbound webhooks (provider to Authpipe)
Section titled “Inbound webhooks (provider to Authpipe)”Some providers send webhooks to Authpipe (e.g., Slack events, GitHub webhooks). These arrive at:
POST https://api.authpipe.dev/webhooks/{provider}Authpipe verifies the webhook signature using the signing_key on the provider config, processes the event, and emits a webhook.received event to your outbound webhook URL.
Configure a signing key
Section titled “Configure a signing key”Set the provider’s signing/verification key on your provider config:
await authpipe.updateProviderConfig("pcfg_abc123", { signing_key: "your-provider-signing-secret",});_, err := client.UpdateProviderConfig(ctx, "pcfg_abc123", &authpipe.UpdateProviderConfigParams{ SigningKey: "your-provider-signing-secret",})client.update_provider_config( config_id="pcfg_abc123", signing_key="your-provider-signing-secret",)Searching events
Section titled “Searching events”Query the event log to audit webhook delivery and credential lifecycle events.
const events = await authpipe.searchEvents({ filter: { event_type: { eq: "connection.failed" }, }, sort: [{ field: "created_at", direction: "desc" }], limit: 20,});resp, err := client.SearchEvents(ctx, &authpipe.SearchRequest{ Filter: map[string]map[string]interface{}{ "event_type": {"eq": "connection.failed"}, }, Sort: []authpipe.SortDirective{{Field: "created_at", Direction: "desc"}}, Limit: 20,})from authpipe.types import SearchRequest, SortDirective
resp = client.search_events(SearchRequest( filter={"event_type": {"eq": "connection.failed"}}, sort=[SortDirective(field="created_at", direction="desc")], limit=20,))