Inbound Webhooks
Receive HTTP POSTs from external systems and start flows
Inbound webhooks let external systems push data into Wexio. Any service that can POST JSON, such as a payment provider, your own backend or a no-code tool, can start a Wexio flow by calling a unique URL.

Request Flow
External System Wexio
│ │
│ POST /webhooks/in/{connectionId} │
│ Auth header (mode-specific) │
│ Body: { ...JSON payload... } │
│ ────────────────────────────────► │
│ │
│ 1. Verify authentication
│ 2. Check payload size (≤100 KB)
│ 3. Parse JSON
│ 4. (Optional) Capture schema
│ 5. Resolve the target contact
│ 6. Find matching flow triggers
│ 7. Start each matching flow
│ │
│ 202 Accepted │
│ ◄──────────────────────────────── │Creating an Inbound Connection
From Settings → Webhooks and API → Webhooks, click New connection and set Direction to Inbound.

Fill in the form:
| Field | What to enter |
|---|---|
| Connection name | The system on the other side, for example "Stripe Payments" or "n8n Lead Pipeline" |
| Contact identifier type | Chat ID, Phone number, Email or Channel user ID. See Contact Resolution |
| JSON path to identifier | Where the identifier sits in the payload, for example $.customer.phone |
| Channel integration | Needed only for Channel user ID |
| Authentication mode | The mode your sender uses, or click a provider chip. See Authentication Modes |
Click Create connection. The toast says Webhook connection created. Wexio generates:
- A unique URL:
https://api.wexio.io/webhooks/in/{connectionId} - A signing secret (for Shared Secret mode)
The next screen shows the Endpoint URL and the Webhook secret, each with a copy button, and the Capture schema and Inject payload buttons.
Copy the secret now. We won't show it again - you'll have to rotate it if lost. If you lose it, open the ... menu on the connection page and click Rotate secret to generate a new x-webhook-secret (see Rotate the Signing Secret).
Your plan caps how many connections you can create. See Connection Limit.
The Connection Page
Click Open on a connection in the list. The header has these buttons:
| Button | What it does |
|---|---|
| Inbound endpoint | Shows the Endpoint URL, the x-webhook-secret header, the optional x-idempotency-key header and a cURL example you can copy |
| Logs | Opens the request log |
| Pause / Resume | Stops or restarts accepting requests |
| ... | Rotate secret and Delete |
Below the header the page has the sections Authentication, Contact resolution, Payload schema, Ingest a sample payload and Used by. The connection page never shows the secret again: The current secret isn't displayed again after creation. Rotate the secret to view a new value.

Authentication Modes
Pick the auth mode your partner system uses. Set one in Authentication mode, or click a chip under Provider presets to set it for you. Later, change it with Change in the Authentication section.

| Mode | When to use | Secret format |
|---|---|---|
| Shared secret (default) | Any system that lets you configure a custom request header - n8n, Zapier, Make, IFTTT, your own backend | Random string generated by Wexio |
| Stripe | Stripe webhooks (Payments, Billing, Connect) | whsec_… from Stripe dashboard |
| Slack | Slack Events API, slash commands, interactive components | "Signing Secret" from the app's Basic Information page |
| Svix (Clerk, Resend, Orb, Knock, Dub, …) | Svix-powered senders, including Clerk and Resend | whsec_… from the provider's dashboard |
| Paddle Billing v2 | Paddle Billing v2 notifications | pdl_ntfset_… from Paddle's Notifications |
| Revolut Business | Revolut Business webhooks | Shown once on webhook creation in Revolut |
| HubSpot (signature v3) | HubSpot webhook subscriptions | Your HubSpot app's client secret |
| Mailgun | Mailgun webhooks | HTTP webhook signing key from Mailgun |
| Zendesk | Zendesk webhooks | Signing secret of the Zendesk webhook |
| Paystack | Paystack webhooks | Your Paystack secret key |
| Vercel | Vercel webhooks | Secret shown once when you create the Vercel webhook |
| GitLab (secret token) | GitLab webhooks | A secret token you choose, the same in GitLab and Wexio |
| Generic HMAC SHA-256 | GitHub, Bitbucket, Shopify, Razorpay, PagerDuty, Linear, Dub, any custom HMAC that signs the raw body with SHA-256 | Provider-specific; you configure the header name and encoding |
Shared Secret
Send the secret as the x-webhook-secret request header on every POST:
POST /webhooks/in/{connectionId}
x-webhook-secret: <your-secret>
Content-Type: application/json
{ ...payload... }Wexio bcrypt-compares the header against the stored hash. There is no per-body signature, so use this mode when HTTPS is your only transport guarantee or when you handle replays with X-Idempotency-Key.
Provider Presets
The New connection modal has a Provider presets section. A chip sets Authentication mode and, for HMAC presets, the header fields. You still paste the provider's signing secret.

| Group | Chips |
|---|---|
| Dedicated providers | Stripe, Paddle Billing, Revolut, Slack, Clerk, Resend, HubSpot, Mailgun, Zendesk, Paystack, Vercel, GitLab |
| HMAC presets | GitHub, Bitbucket, Shopify, Razorpay, PagerDuty, Linear, Dub |
| Not yet supported - use proxy | Netlify, Tipalti, Orb, Knock, Humanloop, Intercom, Discord, SendGrid, PayPal, Wise, Paddle Classic, Apple IAP, Google Play Billing, Payoneer |
The last group cannot be verified yet. Send those providers through a proxy, as described in Not Yet Supported Natively.
Provider Modes
Pick the mode and paste the signing secret from the provider's dashboard. Wexio handles header parsing, the signature check and timing-safe comparison. Modes that sign a timestamp (Stripe, Slack, Svix, Paddle, Revolut, HubSpot, Mailgun, Zendesk) reject requests older than 5 minutes.
| Mode | Notes |
|---|---|
| HubSpot (signature v3) | Checks X-HubSpot-Signature-v3 against the URL HubSpot called. The optional Public URL (optional) field takes the exact https URL you registered in HubSpot. Leave it empty to use this endpoint's address |
| Mailgun | The signature sits in the request body, not in a header. Each signature token is accepted once per connection |
| Zendesk | Checks X-Zendesk-Webhook-Signature with its timestamp header |
| Paystack | Checks x-paystack-signature (HMAC-SHA512 of the body). No timestamp, so no replay protection |
| Vercel | Checks x-vercel-signature (HMAC-SHA1 of the body). No timestamp, so no replay protection |
| GitLab (secret token) | Compares X-Gitlab-Token with your token. GitLab does not sign the body, so there is no replay protection |
Wexio accepts several signatures in one header during a secret rotation, for Svix, Revolut and PagerDuty (Generic HMAC SHA-256 reads them too).

Generic HMAC SHA-256
Use this mode for any provider that signs the raw body with HMAC-SHA256. It has no replay protection, because the signature carries no timestamp. Providers whose signature covers more than the body, or uses another hash, do not verify here. See Custom HMAC for the full setup.

| Field | Description |
|---|---|
| Signing secret | The provider's secret |
| Signature header name | Where the signature lands (for example X-Hub-Signature-256 for GitHub, X-Shopify-Hmac-Sha256 for Shopify) |
| Encoding | Hex (most providers) or Base64 (Shopify) |
| Header prefix (optional) | String to strip before comparing (GitHub prefixes with sha256=) |
The HMAC presets chips fill in these fields for GitHub, Bitbucket, Shopify, Razorpay, PagerDuty, Linear and Dub.
Not Yet Supported Natively
Wexio cannot verify these providers' signatures yet: Netlify, Tipalti, Orb, Knock, Humanloop, Intercom, Discord, SendGrid, PayPal, Wise, Paddle Classic, Apple IAP, Google Play Billing and Payoneer.
Send their events through n8n or Zapier. The proxy receives the event and forwards it to Wexio with the x-webhook-secret header, so the connection uses Shared secret (default).
See Integrations for the full list and per-provider setup steps.
Contact Resolution
Wexio maps each incoming payload to a contact in your organisation using these settings.

| Field | Description |
|---|---|
| Connection name | Display name - only affects the UI |
| Identifier type (Contact identifier type in the create form) | One of Channel user ID, Phone number, Email, Chat ID |
| JSON path to identifier | Where to read the identifier from the payload - JSONPath syntax, e.g. $.customer.email |
| Channel integration | Required when identifier type is Channel user ID - scopes the lookup to a specific Telegram bot / WhatsApp number / Instagram page |
Examples:
| Payload | Identifier type | JSON path | Channel integration |
|---|---|---|---|
| Stripe Customer | Email | $.data.object.email | - |
| Telegram user event | Channel user ID | $.user.id | your Telegram bot |
| WhatsApp callback | Phone number | $.contacts[0].wa_id | your WhatsApp number |
| Internal system | Chat ID | $.wexio_chat_id | - |
If the path returns nothing, or the identifier doesn't match a contact that has a chat, the log is tagged IDENTIFIER_NOT_FOUND and no flow runs. Wexio never creates a contact from a webhook.
Capture Schema
Wexio learns the shape of your payload from a sample, so the fields become flow variables. This step also tests that the partner reaches the URL.

To capture a schema:
- Tap Capture schema on the connection detail page. A 5-minute tunnel opens.
- Trigger a real event on the partner side (or use Inject payload to paste a sample JSON manually).
- Wexio infers the schema from that single payload - every scalar leaf becomes a variable - replaces the current schema (no merge), bumps the version, and closes the tunnel.
- The payload used for capture is not dispatched to flows - capture is a separate path so you can experiment safely.
The Payload Schema panel shows every learned field, its inferred type and whether it is required. Use these fields as {{webhook.<path>}} variables in any flow bound to this connection.

Schema is advisory - Wexio never rejects a payload because a field is missing. The schema only drives the variable picker. Missing fields render as null at runtime.
Inject a Sample Payload
If you cannot trigger the partner on demand, use Inject payload to paste JSON directly. Wexio treats it like a live capture.

Bind the Connection to a Flow
Open any flow, add a Webhook Received trigger (Pro and Enterprise plans), and pick this connection. The flow must be published. It runs each time a matching payload arrives and a contact resolves.
One connection can serve many flows. The connection owns the auth and mapping, and each flow decides what to do with the payload.
Request Headers
| Header | Purpose |
|---|---|
Content-Type: application/json | Required |
x-webhook-secret | Required when auth mode is Shared secret (default) |
Stripe-Signature / X-Slack-Signature / svix-signature / Paddle-Signature / Revolut-Signature / X-HubSpot-Signature-v3 / X-Zendesk-Webhook-Signature / x-paystack-signature / x-vercel-signature / X-Gitlab-Token | Read automatically by the matching provider mode (Mailgun reads its signature from the body) |
x-request-id | Optional correlation ID; echoed in the history log |
X-Idempotency-Key | Optional dedup key - replays within 24 hours return the cached response |
Outcomes & HTTP Responses
Every request lands a row in the history log with one of these outcomes:
| Outcome | Status | Meaning |
|---|---|---|
ACCEPTED | 202 | Authenticated, parsed, at least one flow dispatched |
SAMPLE_CAPTURED | 202 | Absorbed by an open capture tunnel; no flow ran |
UNAUTHORIZED | 401 | Signature / secret mismatch |
INVALID_JSON | 400 | Body was not valid JSON |
PAYLOAD_TOO_LARGE | 413 | Body exceeded 100 KB |
IDENTIFIER_NOT_FOUND | 400 | No contact resolved from the payload |
TRIGGER_NOT_FOUND | 400 | Connection is paused, deleted, or has no bound flow triggers. The errorCode is WEBHOOK_UNAVAILABLE when the plan no longer includes webhooks |
NEWS_INGEST_FAILED | 400 | A News widget bound to the connection rejected the payload (for example a missing items array). Flow triggers on the connection do not run for that payload |
ERROR | 400 | Unclassified failure - file a ticket if this appears |
The endpoint answers only 202, 401, 413 or 400. The errorCode field in the JSON body tells the 400 cases apart.
Rotate the Signing Secret
Settings → Webhooks and API → Webhooks → your connection, then the ... menu in the header and Rotate secret. The menu item exists on inbound connections only. Confirm in Rotate webhook secret? with Rotate secret. The dialog warns that the current secret stops working immediately and that external systems using it get 401 Unauthorized. Rotation generates a new x-webhook-secret for Shared secret mode and shows it once as New secret (Copy it now - we don't show secrets again.), then the toast says Secret rotated. Update your external systems. The connection page never shows it again. The old one stops working immediately, so update the partner configuration first or accept a short window of 401s.
Stripe, Slack, Paddle, Revolut, PagerDuty and several other providers support dual-signing during rotation. Use that window on their side and paste the new secret into Wexio last, with Change under Authentication.
Pause & Resume
To stop accepting requests without deleting the connection, click Pause on the connection detail page. A paused connection returns 400 with errorCode: TRIGGER_NOT_FOUND to every request. You can resume at any time.
Change Authentication
In the Authentication section, click Change to open Change authentication. Pick the Authentication mode (or a provider chip), then paste the Signing secret from your provider. For Generic HMAC SHA-256, also set the Signature header name, Encoding and the optional Header prefix. The stored secret is never shown again: leave the field empty to keep it, or paste a new value to replace it. Click Save changes; a successful change shows Authentication updated.
When you create a connection in a provider mode, the Signing secret is required: without it you see Paste the signing secret from your provider.
Delete a Connection
Open the ... menu and click Delete, then Delete connection in Delete webhook connection?. The sender starts receiving 400 with errorCode: TRIGGER_NOT_FOUND. If flows use the connection, the dialog warns that their webhook triggers stop firing. The toast says Webhook connection deleted.
Rules and Caveats
- Payload limit: 100 KB. Larger payloads are rejected with
413. - Credentials are never returned. The API does not expose the stored signing secret.
- Signature headers are not logged.
x-webhook-secret,Stripe-Signature,Authorization, and similar are scrubbed on the server before the history log is written. - Retention: 7 days of history per connection, including auth failures for self-diagnosis.
Related
- Integrations - per-provider setup and the support matrix
- Custom HMAC - configure any HMAC-SHA256 provider
- Webhook Received - the trigger that starts your flow
- Webhook History - every request and its outcome