Webhooks
Webhooks
Webhooks let external services (GitHub, Stripe, Zapier, etc.) push data into your Web-Nothing apps. Each incoming payload creates a record in the target data table and can trigger automated workflows.
Quick Start
- Create a webhook on your app (from the workspace or via API)
- Configure the external service to POST to the webhook URL
- Include the shared secret in the
x-webhook-secretheader
Creating a Webhook
From the Workspace
- Open your app in the editor.
- Go to the app's settings panel.
- Add a new webhook with a name, target data table, and a secret (minimum 8 characters).
Via API
curl -X POST https://web-nothing.vercel.app/api/apps/APP_ID/webhooks \
-H "Content-Type: application/json" \
-H "Cookie: authjs.session-token=SESSION" \
-d '{
"name": "GitHub Push Events",
"targetSchema": "events",
"secret": "my-secure-secret-123"
}'Required fields:
| Field | Type | Constraints |
|---|---|---|
name | string | 1-255 characters |
targetSchema | string | Must match a data table name in your app |
secret | string | Minimum 8 characters, stored as SHA-256 hash |
Response (201):
{
"webhook": {
"id": "clxyz...",
"name": "GitHub Push Events",
"targetSchema": "events",
"enabled": true,
"hasSecret": true,
"url": "/api/apps/APP_ID/webhook",
"createdAt": "2026-03-17T12:00:00.000Z"
}
}Receiving Events
External services POST JSON payloads to the webhook receiver endpoint. No session auth is required -- the shared secret authenticates the request.
curl -X POST https://web-nothing.vercel.app/api/apps/APP_ID/webhook \
-H "Content-Type: application/json" \
-H "x-webhook-secret: my-secure-secret-123" \
-d '{
"repo": "octocat/Hello-World",
"branch": "main",
"commit": "abc123",
"message": "Update README"
}'Response (201):
{
"success": true,
"id": "generated-uuid"
}The payload fields are written directly as record fields in the target data table. System fields (id, _webhook, created_at, updated_at) are added automatically and cannot be overridden by the caller.
Secret Verification
Every inbound webhook request must include the x-webhook-secret header. The server:
- Hashes the provided secret with SHA-256
- Compares it against the stored hash using constant-time comparison (prevents timing attacks)
- Returns
401 Unauthorizedif the secret is missing or does not match
Secrets are never stored in plaintext -- only the SHA-256 hash is persisted. The hash is never returned in any API response.
Limits
| Limit | Value |
|---|---|
| Webhooks per app | 5 |
| Requests per app | 30/min |
| Requests per IP | 100/min (global) |
| Payload size | 64 KB |
| Records per data table | 10,000 |
Exceeding rate limits returns 429 Too Many Requests. Exceeding payload size returns 413 Payload Too Large. Hitting the record cap returns 429 with "Record limit reached".
Workflow Integration
Each received webhook fires two workflow triggers (non-blocking):
record_created-- standard data trigger, same as manual record creationwebhook_received-- webhook-specific trigger for dedicated automation
Both triggers pass the full new record (including system fields) as the trigger payload. Use these in workflow rules to send notifications, transform data, call external APIs, or chain further actions.
Managing Webhooks
List Webhooks
curl https://web-nothing.vercel.app/api/apps/APP_ID/webhooks \
-H "Cookie: authjs.session-token=SESSION"Returns all webhooks for the app with stats (receivedCount, lastReceivedAt). The secret is never exposed -- only a hasSecret boolean is returned.
Enable / Disable
curl -X PUT https://web-nothing.vercel.app/api/apps/APP_ID/webhooks/WEBHOOK_ID \
-H "Content-Type: application/json" \
-H "Cookie: authjs.session-token=SESSION" \
-d '{ "enabled": false }'Disabled webhooks still exist but the receiver endpoint returns 404 for them.
Update
You can update name, targetSchema, enabled, and secret in a single PUT request. Only include the fields you want to change.
curl -X PUT https://web-nothing.vercel.app/api/apps/APP_ID/webhooks/WEBHOOK_ID \
-H "Content-Type: application/json" \
-H "Cookie: authjs.session-token=SESSION" \
-d '{
"name": "Renamed Webhook",
"targetSchema": "new_table",
"secret": "new-secret-value"
}'Delete
curl -X DELETE https://web-nothing.vercel.app/api/apps/APP_ID/webhooks/WEBHOOK_ID \
-H "Cookie: authjs.session-token=SESSION"Returns { "success": true }.
Example: GitHub Push Webhook
1. Create the webhook in Web-Nothing:
curl -X POST https://web-nothing.vercel.app/api/apps/APP_ID/webhooks \
-H "Content-Type: application/json" \
-H "Cookie: authjs.session-token=SESSION" \
-d '{
"name": "GitHub Pushes",
"targetSchema": "push_events",
"secret": "gh-webhook-secret-2026"
}'2. Configure GitHub:
In your GitHub repo, go to Settings > Webhooks > Add webhook:
- Payload URL:
https://web-nothing.vercel.app/api/apps/APP_ID/webhook - Content type:
application/json - Secret: GitHub uses
X-Hub-Signature-256by default, but Web-Nothing expectsx-webhook-secret. Use a proxy or middleware to forward the raw secret in thex-webhook-secretheader.
3. Test with curl:
curl -X POST https://web-nothing.vercel.app/api/apps/APP_ID/webhook \
-H "Content-Type: application/json" \
-H "x-webhook-secret: gh-webhook-secret-2026" \
-d '{
"ref": "refs/heads/main",
"repository": "octocat/Hello-World",
"pusher": "octocat",
"head_commit_message": "Fix typo in README"
}'4. Verify: Check your app in the workspace -- a new record appears in the push_events data table with your payload fields plus id, _webhook, created_at, and updated_at.
Error Responses
| Status | Meaning |
|---|---|
400 | Invalid JSON, missing fields, body not an object, or data table not found |
401 | Missing or invalid x-webhook-secret header |
404 | App not found, not active, or no enabled webhook |
413 | Payload exceeds 64 KB |
429 | Rate limit exceeded or record cap reached |
500 | Internal error |
Related
| Topic | Document |
|---|---|
| Cross-app automation | Workflows |
| Connecting external APIs | API Connectors |
| Connecting AI agents to your app | MCP Quick Start |
| Full MCP reference | MCP Integration Guide |
| Full user guide | User Guide |