Skip to main content
Time: 20 minutes | Level: Expert
Prerequisites: Sharing & Collaboration, Scheduled Reports
Requires: Pro plan or higher

What You’ll Learn

  • Configure webhooks for real-time data sync
  • Verify webhook signatures for security
  • Build automations with Zapier, Make, and n8n
  • Handle retries and error scenarios

Understanding Webhooks

Webhooks let Peanuts push data to your systems instantly when events happen—no polling required.

Supported Events

EventDescription
entry.createdNew entry added
entry.updatedEntry modified
entry.deletedEntry removed

Setting Up Your First Webhook

1

Open Helper Settings

Navigate to your helper → Settings (gear icon) → Integrations
2

Add Webhook URL

Enter your endpoint URL (must be HTTPS):
https://your-server.com/api/peanuts-webhook
3

Select Events

Choose which events trigger the webhook:
  • ✅ Entry created
  • ✅ Entry updated
  • ☐ Entry deleted
4

Save and Test

Click Save, then Send Test to verify connection.

Webhook Payload

When an event fires, Peanuts sends a POST request:
{
  "event": "entry.created",
  "timestamp": "2026-01-15T14:30:00Z",
  "helper": {
    "id": "abc123",
    "name": "Expense Tracker",
    "command": "/expenses"
  },
  "entry": {
    "id": "entry_456",
    "created_at": "2026-01-15T14:30:00Z",
    "data": {
      "amount": 45.50,
      "category": "Food",
      "description": "Team lunch"
    }
  },
  "user": {
    "id": "user_789"
  }
}

Security: Signature Verification

Always verify webhook signatures in production to ensure requests come from Peanuts.
Peanuts signs every request with HMAC-SHA256. The signature is in the X-Peanuts-Signature header.

Finding Your Secret

  1. Go to helper SettingsIntegrations
  2. Click Show Secret next to your webhook
  3. Copy for use in verification code

Verification Code

const crypto = require('crypto');

function verifyWebhook(req, secret) {
  const signature = req.headers['x-peanuts-signature'];
  const payload = JSON.stringify(req.body);
  
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express middleware
app.post('/webhook', (req, res) => {
  if (!verifyWebhook(req, process.env.PEANUTS_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  const { event, entry } = req.body;
  console.log(`Received ${event}:`, entry.data);
  
  res.status(200).json({ received: true });
});

No-Code Integrations

Zapier

1

Create a Zap

Go to zapier.com → Create Zap
2

Choose Trigger

Select Webhooks by ZapierCatch Hook
3

Copy URL

Zapier provides a unique webhook URL
4

Add to Peanuts

Paste URL in your helper’s webhook settings
5

Test and Build

Add a test entry, then build your action (Google Sheets, Slack, etc.)
Popular Zaps:
  • Expense → Google Sheets row
  • Task completed → Slack notification
  • New entry → Airtable record

Make (Integromat)

1

Create Scenario

New scenario → WebhooksCustom webhook
2

Get URL

Click Add to generate webhook URL
3

Connect

Add URL to Peanuts, send test entry
4

Redetermine Structure

Click Redetermine data structure in Make

n8n (Self-Hosted)

Webhook Node → Set webhook path → Copy production URL → Add to Peanuts
n8n can connect directly to Lovable via MCP for enhanced automation.

Retry Policy

Failed webhooks retry automatically:
AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
After 3 failures, the webhook is marked as failed. Check View History for details.

Best Practices

Respond Quickly

Return 200 immediately, process async. Timeout is 30 seconds.

Implement Idempotency

Use entry.id to detect duplicates and prevent double-processing.

Log Everything

Store raw payloads for debugging failed deliveries.

Use HTTPS

Only HTTPS endpoints are accepted for security.

Quick Response Pattern

app.post('/webhook', async (req, res) => {
  // Verify first
  if (!verifyWebhook(req, secret)) {
    return res.status(401).send();
  }
  
  // Queue for async processing
  await queue.add('process-entry', req.body);
  
  // Respond immediately
  res.status(200).json({ queued: true });
});

Troubleshooting

  1. Verify webhook is enabled in settings
  2. Check URL is correct and accessible
  3. Ensure endpoint returns 2xx status
  4. Review webhook history for errors
  1. Use raw request body, not parsed JSON
  2. Check secret has no extra whitespace
  3. Verify SHA256, not SHA1
  4. Use timing-safe comparison
Implement idempotency:
const processed = new Set();
if (processed.has(entry.id)) return;
processed.add(entry.id);

Exercise

Practice: Expense to Google Sheets

  1. Create a Zapier account (free tier works)
  2. Set up a “Catch Hook” trigger
  3. Add the webhook URL to your Expense Tracker
  4. Create action: Add row to Google Sheets
  5. Log 3 expenses and verify they appear in your sheet
Bonus: Add a filter to only sync expenses over $50

Key Takeaways

Remember: Webhooks enable real-time sync. Always verify signatures, respond quickly, and handle retries gracefully.

Next Steps