Skip to main content
Parchment Health uses webhooks to notify your application in real-time when events happen in your account. This guide will help you integrate with our webhook system to receive and process event notifications.

Overview

  • Delivery: HTTP POST requests to your configured endpoint
  • Retry Logic: Automatic retries with exponential backoff
  • Supported Events: Prescription lifecycle events

How Webhooks Work

  1. An event occurs in Parchment (e.g., a prescription is created)
  2. Parchment sends an HTTPS POST request to your webhook endpoint
  3. Your application processes the event
  4. Your endpoint returns a 2xx status code to confirm receipt

How to Activate Webhooks

1

Provide your webhook endpoint

Contact Parchment support at hello@parchment.health with:
  • Your webhook endpoint URL (must be HTTPS)
  • Your partner ID
# Example
Partner ID: yourpartner
Webhook URL: https://api.yourapp.com/webhooks/parchment
2

Receive your webhook secret

Parchment registers your endpoint and provides a confirmation with your webhook secret. This secret is shown only once — store it securely.
{
  "partner_id": "demo",
  "webhook_url": "https://api.yourapp.com/webhooks/parchment",
  "webhook_secret": "whsec_a1b2c3d4e5f6...",
  "message": "Webhook registered successfully. Store this secret securely - it will not be shown again."
}
Save the webhook_secret immediately. It cannot be retrieved again. If lost, contact Parchment support to rotate it.
3

Implement your webhook handler

Build an endpoint that verifies the signature and routes events by type. Here’s a complete Node.js example:
import express from 'express';
import crypto from 'crypto';

const app = express();

// Retrieve from your secrets manager (e.g. AWS Secrets Manager, HashiCorp Vault)
let WEBHOOK_SECRET: string;

async function start() {
  WEBHOOK_SECRET = await getSecret('parchment-webhook-secret');
  app.listen(3000);
}

app.post('/webhooks/parchment',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-webhook-signature'] as string;
    const payload = req.body.toString('utf8');

    // Step 1: Verify the signature
    const result = verifyWebhook(payload, signature, WEBHOOK_SECRET);
    if (!result.valid) {
      console.error('Signature verification failed:', result.error);
      return res.status(400).json({ error: 'Invalid signature' });
    }

    // Step 2: Parse and route by event type
    const event = JSON.parse(payload);

    switch (event.event_type) {
      case 'prescription.created':
        handlePrescriptionCreated(event);
        break;
      default:
        console.log('Unhandled event type:', event.event_type);
    }

    // Step 3: Acknowledge receipt
    res.status(200).json({ received: true });
  }
);

function verifyWebhook(
  payload: string,
  signatureHeader: string,
  secret: string,
  toleranceSeconds = 300
): { valid: boolean; error?: string } {
  const parts = signatureHeader.split(',');
  let timestamp: number | null = null;
  let signature: string | null = null;

  for (const part of parts) {
    const [key, value] = part.split('=');
    if (key === 't') timestamp = parseInt(value, 10);
    else if (key === 'v1') signature = value;
  }

  if (!timestamp || !signature || isNaN(timestamp)) {
    return { valid: false, error: 'Invalid signature header format' };
  }

  // Check timestamp tolerance (prevents replay attacks)
  if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > toleranceSeconds) {
    return { valid: false, error: 'Timestamp expired' };
  }

  // Compute expected signature
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex');

  // Timing-safe comparison
  const isValid = crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );

  return isValid ? { valid: true } : { valid: false, error: 'Signature mismatch' };
}

function handlePrescriptionCreated(event: any) {
  console.log('New prescription:', event.data.scid);
  // Fetch full details via API using the SCID
}

start();
See Webhook Signature Verification for Python examples and more details.
4

Test your integration

Once deployed, verify that:
  • Your endpoint returns a 2xx status code
  • Signature verification passes
  • Events are routed correctly

Webhook Endpoint Requirements

Your webhook endpoint must:
  • Use HTTPS with a valid SSL certificate
  • Return a 2xx response within 10 seconds — process events asynchronously if needed
  • Handle retries idempotently — use the event_id to prevent duplicate processing

Event Types

Parchment sends webhooks for these event types:
Event TypeDescriptionStatus
prescription.createdA new prescription was createdLive
prescription.updatedA prescription was modifiedComing Soon
prescription.ceasedA prescription was ceasedComing Soon
prescription.reissuedA prescription was reissuedComing Soon
See Webhook Events for detailed payload examples.