Send events from your backend systems, CRM, or automation platforms (Zapier, Make, n8n) directly to inPIPE for processing and forwarding to your connected platforms (Google Analytics 4, Facebook Ads, etc.).
Quick Start #
1. Enable Custom Webhook #
- Go to inPIPE > Connections in your WordPress admin
- Click Configure on the Custom Webhook card
- Toggle Enable Custom Webhook to ON
- Copy your API Key and Endpoint URL
2. Send Your First Event #
curl -X POST "https://yoursite.com/wp-json/inpipe/v1/webhook/events" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"event_name":"purchase","event_id":"order_12345","timestamp":1706918400,"webhook_source":"my_backend","value":99.99,"currency":"USD"}'3. Verify #
Check the Tracked Events page in inPIPE admin to see your event with source custom_webhook.
Authentication #
Include your API key in the X-API-Key header:
POST /wp-json/inpipe/v1/webhook/events
Content-Type: application/json
X-API-Key: cwh_your_api_key_hereAll requests must use HTTPS. Store your API key securely – use environment variables or a secrets manager, never hardcode it.
Event Payload #
Required Fields #
| Field | Type | Description | Example |
|---|---|---|---|
event_name | string | What happened | "purchase", "signup" |
event_id | string | Unique identifier | "order_12345" |
timestamp | integer | Unix timestamp in seconds | 1706918400 |
Timestamp Format #
| Language | Code |
|---|---|
| JavaScript | Math.floor(Date.now() / 1000) |
| Python | int(time.time()) |
| PHP | time() |
Common mistake: JavaScript Date.now() returns milliseconds. Always divide by 1000.
Common Fields #
webhook_source (Recommended) #
Identifies the source system sending the event. Used for filtering and analytics in BigQuery.
| Scenario | Value |
|---|---|
You send webhook_source | Your value (e.g., "salesforce", "hubspot", "zapier") |
| You do NOT send it | Fallback: "unknown-{api_key_id}" (e.g., "unknown-cwh_25959523") |
Note: webhook_source and items are pass-through fields – they are NOT stripped as reserved fields.
Other Common Fields #
| Field | Type | Description |
|---|---|---|
user_id | string | Your internal user ID |
email | string | User email (SHA-256 hash recommended) |
client_id | string | GA4 client ID (if available) |
value | number | Monetary value |
currency | string | ISO currency code (USD, EUR) |
transaction_id | string | Order/transaction ID |
Platform Click IDs #
| Field | Description |
|---|---|
gclid | Google Click ID |
fbclid | Facebook Click ID |
fbc | Facebook Cookie (_fbc) |
fbp | Facebook Pixel (_fbp) |
ttclid | TikTok Click ID |
msclkid | Microsoft/Bing Click ID |
UTM Parameters #
utm_source, utm_medium, utm_campaign, utm_term, utm_content
User Data (for Conversion APIs) #
email, phone, first_name, last_name, city, state, country, zip
Custom Fields #
Any field not in the standard fields list is treated as a custom field and stored in BigQuery.
Limits #
| Custom Fields Sent | Behavior |
|---|---|
| 1-30 | All accepted and stored |
| 31-100 | First 30 accepted, extras stripped (logged server-side) |
| >100 | Rejected with 413 error: “Custom fields payload too large” |
Standard fields (not counted as custom): event_name, event_id, timestamp, webhook_source, user_id, email, client_id, value, currency, transaction_id, user_agent, fbc, fbp, fbclid, gclid, ttclid, msclkid, phone, first_name, last_name, city, state, country, zip, utm_source, utm_medium, utm_campaign, utm_term, utm_content, items.
Querying Custom Fields in BigQuery #
SELECT JSON_VALUE(custom_fields, "$.webinar_id") as webinar_id
FROM outpipe_events
WHERE webhook_source = "hubspot"Items Array (E-commerce) #
For purchase, add_to_cart, and other e-commerce events. Max 30 items per event.
| Field | Required | Description |
|---|---|---|
item_id | Yes | Product SKU or ID |
item_name | Yes | Product name |
price | No | Unit price |
quantity | No | Quantity (default: 1) |
item_category | No | Product category |
item_brand | No | Brand name |
Standard Events #
| Event Name | GA4 | |
|---|---|---|
page_view | page_view | PageView |
purchase | purchase | Purchase |
add_to_cart | add_to_cart | AddToCart |
begin_checkout | begin_checkout | InitiateCheckout |
generate_lead | generate_lead | Lead |
signup | sign_up | CompleteRegistration |
view_item | view_item | ViewContent |
Error Handling #
Response Codes #
| Code | Error Code | Meaning |
|---|---|---|
202 | – | Event queued successfully |
400 | validation_error | Check payload format |
401 | invalid_api_key | Check API key |
403 | webhook_disabled | Webhook is disabled |
409 | duplicate_event | Same event_id sent within 24 hours |
413 | payload_too_large | Payload exceeds 10 KB |
413 | custom_fields_too_large | More than 100 custom fields sent |
429 | rate_limit_exceeded | Rate limit hit (60/min, 500/hr, 5000/day) |
429 | quota_exceeded | Monthly event quota exhausted |
500 | queue_error | Server error – retry with backoff |
Rate Limits #
| Limit | Value |
|---|---|
| Per minute | 60 requests |
| Per hour | 500 requests |
| Per 24 hours | 5,000 requests |
| Payload size | 10 KB max |
| Custom fields | 30 max (31-100 stripped, >100 rejected) |
| Items array | 30 items max |
Rate Limit Headers #
X-RateLimit-Limit-Minute: 60
X-RateLimit-Remaining-Minute: 42
X-RateLimit-Limit-Hour: 500
X-RateLimit-Remaining-Hour: 380
X-RateLimit-Limit-Day: 5000
X-RateLimit-Remaining-Day: 4200
X-RateLimit-Reset: 1706918460Test Mode #
Validate payload without queuing:
curl -X POST "https://yoursite.com/wp-json/inpipe/v1/webhook/events" \
-H "X-API-Key: cwh_your_api_key" \
-H "X-Test-Mode: true" \
-d '{"event_name":"test","event_id":"test_123","timestamp":1706918400}'Code Examples #
Node.js #
const response = await fetch(ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": API_KEY
},
body: JSON.stringify({
event_name: "purchase",
event_id: `order_${Date.now()}`,
timestamp: Math.floor(Date.now() / 1000),
webhook_source: "my_app",
value: 99.99,
currency: "USD"
})
});Python #
import requests, time
response = requests.post(
ENDPOINT,
headers={"Content-Type": "application/json", "X-API-Key": API_KEY},
json={
"event_name": "purchase",
"event_id": f"order_{int(time.time())}",
"timestamp": int(time.time()),
"webhook_source": "my_app",
"value": 99.99,
"currency": "USD"
}
)PHP #
$ch = curl_init($endpoint);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
"event_name" => "purchase",
"event_id" => "order_" . time(),
"timestamp" => time(),
"webhook_source" => "my_app",
"value" => 99.99,
"currency" => "USD"
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: " . $apiKey
],
CURLOPT_RETURNTRANSFER => true
]);
$response = curl_exec($ch);Troubleshooting #
“401 Unauthorized” #
- Check API key is correct
- Ensure no extra whitespace in header
- Verify key has not been regenerated
“409 Conflict” #
- Same event_id sent within 24 hours
- Use unique event IDs
“413 Custom fields payload too large” #
- More than 100 custom fields sent
- Reduce to 30 or fewer custom fields
“429 Rate limit exceeded” #
- Check X-RateLimit-Remaining headers
- Wait until X-RateLimit-Reset timestamp
- Implement exponential backoff
“429 quota_exceeded” #
- Monthly subscription quota exhausted
- Contact admin to check event limits
Events not appearing #
- Check Tracked Events page first (source: custom_webhook)
- Verify outPIPE connections are active
- Allow 5-15 minutes for processing
- Include platform IDs (fbc, gclid, email) for attribution
Security Checklist #
- API key in environment variable (not hardcoded)
- HTTPS for all requests
- Hash email/phone with SHA-256 (64-char lowercase hex)
- Consistent identifiers across related events
- Test mode validation before production