Private BetaProposeFlow is currently in private beta.Join the waitlist

Webhooks

Receive real-time notifications when events occur in your ProposeFlow account. Webhooks let you integrate with your existing systems and trigger automated workflows.

Available Events

EventDescription
proposal.createdA new proposal was generated
proposal.approvedA proposal was approved by a user
proposal.rejectedA proposal was rejected by a user
proposal.expiredA proposal expired without a decision
event.completedAn event was processed successfully
event.failedAn event failed to process
proposals.generatedProposals were generated from an event

Creating a Webhook

create-webhook.ts
const webhook = await pf.webhooks.create({
  url: 'https://your-app.com/api/webhooks/proposeflow',
  events: ['proposal.approved', 'proposal.rejected'],
  secret: 'whsec_your_secret_key',  // For signature verification
});

console.log(webhook.id);  // 'wh_abc123'

Webhook Payload

Webhook payloads include the event type, timestamp, and relevant data.

payload.json
{
  "id": "evt_abc123",
  "type": "proposal.approved",
  "created": "2024-01-15T10:30:00Z",
  "data": {
    "proposal": {
      "id": "prop_xyz789",
      "schemaId": "blogPost",
      "status": "approved",
      "data": {
        "title": "Understanding TypeScript",
        "content": "TypeScript is a typed superset...",
        "tags": ["typescript", "programming"]
      },
      "decidedAt": "2024-01-15T10:30:00Z",
      "decidedBy": "user_123"
    }
  }
}

event.completed Payload

event-completed.json
{
  "id": "evt_abc123",
  "type": "event.completed",
  "created": "2024-01-15T10:30:00Z",
  "data": {
    "eventId": "pf_evt_abc123",
    "status": "skipped",
    "proposalCount": 0,
    "cacheHit": true,
    "cacheKey": "b2b8c7b1e9c84c3e1c4f4a5a7e9b9c3f1fcd2e0b9b1e7b8a3b5f64c1d2a9e7f0",
    "coalescedEventIds": ["pf_evt_old1", "pf_evt_old2"]
  }
}

Verifying Signatures

Always verify webhook signatures to ensure requests are authentic. ProposeFlow signs payloads with HMAC-SHA256.

verify.ts
import { verifyWebhookSignature } from '@proposeflow/sdk';

app.post('/api/webhooks/proposeflow', async (req, res) => {
  const signature = req.headers['x-proposeflow-signature'];
  const timestamp = req.headers['x-proposeflow-timestamp'];
  const body = req.rawBody;  // Must be raw body, not parsed JSON

  const isValid = verifyWebhookSignature({
    payload: body,
    signature,
    timestamp,
    secret: process.env.WEBHOOK_SECRET!,
  });

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process the webhook
  const event = JSON.parse(body);
  // ...

  res.status(200).send('OK');
});

Next.js API Route Example

app/api/webhooks/proposeflow/route.ts
import { verifyWebhookSignature } from '@proposeflow/sdk';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(req: NextRequest) {
  const body = await req.text();
  const signature = req.headers.get('x-proposeflow-signature')!;
  const timestamp = req.headers.get('x-proposeflow-timestamp')!;

  const isValid = verifyWebhookSignature({
    payload: body,
    signature,
    timestamp,
    secret: process.env.WEBHOOK_SECRET!,
  });

  if (!isValid) {
    return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
  }

  const event = JSON.parse(body);

  switch (event.type) {
    case 'proposal.approved':
      await handleApproval(event.data.proposal);
      break;
    case 'proposal.rejected':
      await handleRejection(event.data.proposal);
      break;
  }

  return NextResponse.json({ received: true });
}

Retry Behavior

ProposeFlow automatically retries failed webhook deliveries with exponential backoff.

  • Webhooks are retried up to 5 times
  • Retry delays: 1min, 5min, 30min, 2hr, 24hr
  • A 2xx response is considered successful
  • Requests timeout after 30 seconds

Managing Webhooks

manage.ts
// List all webhooks
const webhooks = await pf.webhooks.list();

// Update a webhook
await pf.webhooks.update('wh_abc123', {
  events: ['proposal.approved'],  // Only receive approvals
});

// Disable a webhook
await pf.webhooks.update('wh_abc123', {
  enabled: false,
});

// Delete a webhook
await pf.webhooks.delete('wh_abc123');