⚡ Webhooks

Webhooks

Webhooks let Proxiant notify your systems when events happen — no polling required. When a job is served, an affidavit is generated, or a server accepts a Hive job, we POST a JSON payload to your endpoint immediately.

Overview

📬
Delivery
POST to your endpoint with JSON payload
🔁
Retry Policy
3 retries: immediately, +5 min, +30 min on non-2xx
🔒
Signature
HMAC-SHA256 via X-Proxiant-Signature

Your endpoint must respond with a 2xx status code within 10 seconds to acknowledge delivery. If it doesn't, Proxiant will retry. After all retries are exhausted, the webhook delivery is marked failed and the last_delivery_status on the webhook object is updated.

💡
Make your endpoint idempotent. Due to retries, you may receive the same event more than once. Use the event id field to deduplicate deliveries.

List Webhooks

GET /webhooks

Returns all webhook subscriptions for your firm.

bash
curl https://api.proxiant.co/v1/webhooks \
  -H "Authorization: Bearer prx_live_xxxxxxxxxxxx"
json — Response
{
  "data": [
    {
      "id":                  "wh_abc123-1111-2222-3333-444455556666",
      "url":                 "https://app.example.com/webhooks/proxiant",
      "events":             ["job.served", "attempt.logged"],
      "active":             true,
      "created_at":         "2025-03-01T12:00:00Z",
      "last_delivery_at":   "2025-04-15T09:22:11Z",
      "last_delivery_status": "success"
    }
  ]
}

Create Webhook

POST /webhooks

Creates a new webhook subscription. Your endpoint will be called immediately with a webhook.test ping event upon creation.

Request Body

FieldTypeRequiredDescription
urlstringrequiredHTTPS endpoint to deliver events to
eventsarrayrequiredArray of event names to subscribe to
secretstringoptionalSecret for HMAC signature verification. Strongly recommended.
bash
curl -X POST https://api.proxiant.co/v1/webhooks \
  -H "Authorization: Bearer prx_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://app.example.com/webhooks/proxiant",
    "events": ["job.served", "job.non_est", "attempt.logged", "affidavit.generated"],
    "secret": "whsec_your_signing_secret_here"
  }'

Delete Webhook

DELETE /webhooks/:id

Deletes a webhook subscription. Event delivery to this URL stops immediately.

bash
curl -X DELETE https://api.proxiant.co/v1/webhooks/wh_abc123-1111-2222-3333-444455556666 \
  -H "Authorization: Bearer prx_live_xxxxxxxxxxxx"

Returns 204 No Content on success.

Event Catalog

Subscribe to any combination of these events when creating a webhook. You can use "events": ["*"] to subscribe to all events.

EventTriggered whenPayload includes
job.created
Jobs
A new job is created in your firmFull job object
job.updated
Jobs
Job fields or status changesJob object + changed_fields array
job.served
Jobs
Job is marked as successfully servedJob object + serving attempt object
job.non_est
Jobs
Job is marked non-est (unable to serve)Job object
job.overdue
Jobs
Job passes its due date without being servedJob object
attempt.logged
Attempts
A new service attempt is loggedAttempt object + parent job object
invoice.created
Billing
An invoice is generated for a clientInvoice object
invoice.paid
Billing
An invoice payment is recordedInvoice object + payment object
affidavit.generated
Affidavits
An affidavit PDF is generated and readyAffidavit object + job object
hive.job_posted
The Hive
A job is posted to The Hive marketplaceJob object
hive.job_accepted
The Hive
A server accepts a job from The HiveJob object + server object
eservice.accepted
eService
Recipient accepts electronic serviceJob object + eservice object

Signature Verification

When you provide a secret when creating a webhook, Proxiant signs every delivery with an X-Proxiant-Signature header. This lets you verify that the request actually came from Proxiant and wasn't tampered with.

The signature is computed as: HMAC-SHA256(raw_request_body, secret), hex-encoded, prefixed with sha256=.

node.js
const crypto = require('crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expectedSig = 'sha256=' +
    crypto.createHmac('sha256', secret)
      .update(rawBody)
      .digest('hex');

  // Timing-safe comparison prevents timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(expectedSig),
    Buffer.from(signature)
  );
}

// Express example
app.post('/webhooks/proxiant', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-proxiant-signature'];
  if (!verifyWebhook(req.body, sig, process.env.FINALHIVE_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  // Handle event...
  res.status(200).json({ received: true });
});
python
import hmac, hashlib, os

def verify_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

# Flask example
@app.route("/webhooks/proxiant", methods=["POST"])
def webhook():
    sig = request.headers.get("X-Proxiant-Signature", "")
    if not verify_webhook(request.get_data(), sig, os.environ["FINALHIVE_WEBHOOK_SECRET"]):
        return "Invalid signature", 401
    event = request.get_json()
    # Handle event...
    return {"received": True}, 200

Example Payload

Every event delivery includes a standard envelope with the event type, ID, timestamp, and the event-specific data.

job.served

json — POST body delivered to your endpoint
{
  "id":         "evt_7f3a9b12-cdef-4567-89ab-cdef01234567",
  "event":      "job.served",
  "created_at": "2025-04-15T09:22:15Z",
  "data": {
    "job": {
      "id":             "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d",
      "job_number":     "FH-2025-04812",
      "status":         "served",
      "job_type":       "subpoena",
      "recipient_name": "James R. Thornton",
      "client_id":      "a3c4e5f6-1a2b-3c4d-5e6f-7g8h9i0j1k2l",
      "client_name":    "Harmon & Keyes LLP",
      "updated_at":     "2025-04-15T09:22:11Z"
    },
    "attempt": {
      "id":              "b2c3d4e5-2222-3333-4444-555566667777",
      "sequence_number": 3,
      "outcome":         "served",
      "attempted_at":   "2025-04-15T09:12:00Z",
      "gps_verified":   true,
      "server_name":    "Marcus Webb",
      "narrative":       "Subject answered door, confirmed identity. Documents delivered in hand."
    }
  }
}
Respond quickly. Acknowledge the webhook immediately with a 2xx response, then process it asynchronously. If your handler takes too long, Proxiant may consider the delivery failed and retry — leading to duplicate processing.