GuidesRecipesAPI ReferenceChangelogDiscussions
Log In
Guides

Webhooks

Receive real-time notifications when important events happen during the loan lifecycle. Get instant updates on application status, document signing, funding progress, and more.

Webhooks

Webhooks let you receive automatic notifications when important events happen in Thrive. Instead of repeatedly checking for updates, we'll send the information directly to your system the moment something changes.

Getting Started with Webhooks

You can manage webhook subscriptions directly through our API endpoints, or contact your Thrive representative for assistance. See Managing Webhook Subscriptions below for API details.

What You'll Receive

When an event occurs, Thrive sends an HTTP POST request to your endpoint with a JSON payload containing the event details. All payloads include:

FieldDescription
application_idThe unique identifier for the loan application
statusThe current status or state change
timestampWhen the event occurred (ISO 8601 format)

Additional fields are included depending on the event type.


Available Events

Application Events

These events notify you when a loan application's status or terms change.

application.status.update

Sent when the application status changes (e.g., Approved, Declined, Pending Review).

{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "status": "Approved",
  "application_id": "c4ff1199-a135-4739-b110-91f7c1bf9d2f",
  "data": {
    "approved_offer": {
      "term": "60",
      "interest_rate": "8.99",
      "borrower_max_approved_amount": "25000.00",
      "project_max_approved_amount": "30000.00",
      "max_amount": "25000.00",
      "approved_amount": "25000.00",
      "merchant_fee_percentage": "7.00"
    }
  }
}

Offer Fields Explained:

FieldDescription
termLoan term in months
interest_rateAnnual interest rate (percentage)
borrower_max_approved_amountMaximum amount the borrower qualifies for
project_max_approved_amountMaximum amount allowed for the project
max_amountThe lesser of borrower and project maximums
approved_amountThe actual approved loan amount
merchant_fee_percentageYour merchant fee as a percentage

application.amount.update

Sent when the approved loan amount changes after initial approval.

The payload follows the same structure as application.status.update.


application.offer.update

Sent when loan terms change (rate, term, or fees) after a hard credit pull. This payload includes both the previous and updated offer so you can see exactly what changed.

{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "status": "Terms Updated",
  "application_id": "c4ff1199-a135-4739-b110-91f7c1bf9d2f",
  "data": {
    "previous_approved_offer": {
      "term": "60",
      "interest_rate": "9.99",
      "borrower_max_approved_amount": "20000.00",
      "project_max_approved_amount": "25000.00",
      "approved_amount": "20000.00",
      "merchant_fee_percentage": "7.00"
    },
    "updated_approved_offer": {
      "term": "60",
      "interest_rate": "8.99",
      "borrower_max_approved_amount": "25000.00",
      "project_max_approved_amount": "30000.00",
      "approved_amount": "25000.00",
      "merchant_fee_percentage": "6.50"
    }
  }
}

Document Events

These events track the loan document signing process.

document.sent

Sent when loan documents are delivered to the borrower for e-signature.

{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "status": "DOCUMENT_SENT",
  "application_id": "c4ff1199-a135-4739-b110-91f7c1bf9d2f"
}

document.signed

Sent when the borrower completes signing all required loan documents.

{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "status": "DOCUMENT_SIGNED",
  "application_id": "c4ff1199-a135-4739-b110-91f7c1bf9d2f"
}

Disbursement Events

These events track the funding process from request to settlement.

disbursement.funding.status-changed

Sent when the funding status changes. You'll receive this event when funding is requested and again when it begins processing.

{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "status": "FUNDING_REQUESTED",
  "application_id": "c4ff1199-a135-4739-b110-91f7c1bf9d2f"
}

Possible Status Values:

StatusMeaning
FUNDING_REQUESTEDA funding request has been submitted
FUNDING_PROCESSINGThe funding request is being processed

disbursement.funding.settled

Sent when funds have been successfully transferred.

{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "status": "FUNDING_SETTLED",
  "application_id": "c4ff1199-a135-4739-b110-91f7c1bf9d2f"
}

Task Events

These events notify you when tasks are created or updated on loan applications.

task.created

Sent when a new task is added to an application (e.g., document upload required).

{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "status": "Pending",
  "application_id": "c4ff1199-a135-4739-b110-91f7c1bf9d2f",
  "task_title": "Upload proof of income"
}

task.updated

Sent when a task's status changes (e.g., completed, cancelled).

{
  "timestamp": "2025-01-15T10:30:00.000Z",
  "status": "Completed",
  "application_id": "c4ff1199-a135-4739-b110-91f7c1bf9d2f",
  "task_title": "Upload proof of income"
}

Setting Up Your Endpoint

Endpoint Requirements

Your webhook endpoint must meet these requirements:

  • HTTPS only - We do not send webhooks to HTTP URLs
  • Publicly accessible - Must be reachable from the internet
  • Responds quickly - Return a response within 30 seconds
  • Returns 2xx status - Any 2xx status code confirms successful receipt

Choosing Your Events

When setting up webhooks with your Thrive representative, you can choose to receive:

  • Specific events - Only the events you need (e.g., just disbursement.funding.settled)
  • All events - Every event type listed above

Most integrations subscribe to these key events:

Use CaseRecommended Events
Track funding progressdisbursement.funding.status-changed, disbursement.funding.settled
Monitor full loan lifecycleapplication.status.update, document.signed, disbursement.funding.settled
Stay informed of everythingAll events

Handling Webhooks

Responding to Webhooks

Always return a 200 OK response as quickly as possible. This confirms you received the webhook. If you need to do additional processing, do it after sending the response.

app.post('/webhooks/thrive', (req, res) => {
  // Respond immediately
  res.status(200).json({ received: true });

  // Then process the webhook
  processWebhook(req.body);
});

Processing Different Event Types

You can identify the event type by examining the payload:

app.post('/webhooks/thrive', (req, res) => {
  const { status, application_id, data, task_title } = req.body;

  // Task events include task_title
  if (task_title) {
    console.log(`Task "${task_title}" is now: ${status}`);
  }
  // Application events include offer data
  else if (data?.approved_offer || data?.updated_approved_offer) {
    console.log(`Application ${application_id} updated: ${status}`);
  }
  // Document events have DOCUMENT_ prefix
  else if (status.startsWith('DOCUMENT_')) {
    console.log(`Document status: ${status}`);
  }
  // Disbursement events have FUNDING_ prefix
  else if (status.startsWith('FUNDING_')) {
    console.log(`Funding status: ${status}`);
  }

  res.status(200).json({ received: true });
});

Securing Your Endpoint

HMAC Signature Verification (Recommended)

If you choose HMAC authentication, we'll sign each webhook using a shared secret. The signature is included in the x-hmac-signature header.

To verify the signature, compute the HMAC-SHA256 hash of the raw request body using your secret, then compare it to the signature we sent:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expected, 'hex')
  );
}

app.post('/webhooks/thrive', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-hmac-signature'];

  if (!verifySignature(req.body.toString(), signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Signature valid - process the webhook
  const payload = JSON.parse(req.body);
  // ...
});
import hmac
import hashlib

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Bearer Token Authentication

Alternatively, we can include a Bearer token in the Authorization header. Your endpoint should verify this token matches the one provided during setup.


Handling Retries and Duplicates

Automatic Retries

If your endpoint doesn't respond with a 2xx status code (or times out), we'll retry the webhook up to 3 times. Make sure your endpoint is reliable to avoid missing events.

Duplicate Handling

Because of retries, you may occasionally receive the same webhook more than once. To handle this, track which webhooks you've already processed using the combination of application_id, status, and timestamp:

const processed = new Set();

app.post('/webhooks/thrive', (req, res) => {
  const { application_id, status, timestamp } = req.body;
  const key = `${application_id}:${status}:${timestamp}`;

  if (processed.has(key)) {
    // Already handled this webhook
    return res.status(200).json({ received: true });
  }

  processed.add(key);

  // Process the webhook...

  res.status(200).json({ received: true });
});

For production systems, store processed webhook keys in a database with an expiration time (e.g., 24 hours).


Testing Your Integration

Local Development

Use a tool like ngrok to expose your local development server:

ngrok http 3000

This gives you a public HTTPS URL that forwards to your local machine. Share this URL with your Thrive representative for testing.

Sample Test Payloads

Use these sample payloads to test your webhook handler:

Application Approved:

curl -X POST https://your-endpoint.com/webhooks/thrive \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2025-01-15T10:30:00.000Z",
    "status": "Approved",
    "application_id": "test-app-123",
    "data": {
      "approved_offer": {
        "term": "60",
        "interest_rate": "8.99",
        "approved_amount": "25000.00",
        "merchant_fee_percentage": "7.00"
      }
    }
  }'

Documents Signed:

curl -X POST https://your-endpoint.com/webhooks/thrive \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2025-01-15T10:30:00.000Z",
    "status": "DOCUMENT_SIGNED",
    "application_id": "test-app-123"
  }'

Funding Settled:

curl -X POST https://your-endpoint.com/webhooks/thrive \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2025-01-15T10:30:00.000Z",
    "status": "FUNDING_SETTLED",
    "application_id": "test-app-123"
  }'

Quick Reference

All Event Types

EventWhen It's Sent
application.status.updateApplication status changes (Approved, Declined, etc.)
application.amount.updateApproved loan amount is modified
application.offer.updateLoan terms change after hard pull
document.sentDocuments sent to borrower for signing
document.signedBorrower completes document signing
disbursement.funding.status-changedFunding requested or processing
disbursement.funding.settledFunds successfully transferred
task.createdNew task added to application
task.updatedTask status changes

All Status Values

CategoryPossible Values
DocumentDOCUMENT_SENT, DOCUMENT_SIGNED
FundingFUNDING_REQUESTED, FUNDING_PROCESSING, FUNDING_SETTLED

Managing Webhook Subscriptions

You can create, update, and manage webhook subscriptions programmatically using our API. This gives you full control over your webhook configuration without needing to contact support.

Authentication

All webhook subscription endpoints require the standard Thrive API authentication headers:

HeaderDescription
x-thrive-client-idYour API client ID
x-thrive-client-secretYour API client secret
x-thrive-merchant-uidYour merchant unique identifier

Available Endpoints

MethodEndpointDescription
GET/public/v1/webhook-subscriptionsList all your webhook subscriptions
GET/public/v1/webhook-subscriptions/event-typesList available event types
GET/public/v1/webhook-subscriptions/eventsView webhook delivery history
GET/public/v1/webhook-subscriptions/{id}Get a specific subscription
POST/public/v1/webhook-subscriptionsCreate a new subscription
PUT/public/v1/webhook-subscriptions/{id}Update an existing subscription
DELETE/public/v1/webhook-subscriptions/{id}Delete a subscription

Creating a Subscription

curl -X POST https://api.thrive.com/public/v1/webhook-subscriptions \
  -H "Content-Type: application/json" \
  -H "x-thrive-client-id: your-client-id" \
  -H "x-thrive-client-secret: your-client-secret" \
  -H "x-thrive-merchant-uid: your-merchant-uid" \
  -d '{
    "name": "Production Webhook",
    "description": "Receives funding status updates",
    "url": "https://api.example.com/webhooks/thrive",
    "secret": "whsec_your_secret_here",
    "subscriptions": [
      "disbursement.funding.status-changed",
      "disbursement.funding.settled",
      "application.status.update"
    ],
    "enabled": true,
    "auth_type": "hmac-sha256"
  }'

Subscription Fields

FieldRequiredDescription
nameYesHuman-readable name (max 255 characters)
descriptionNoOptional description (max 1000 characters)
urlYesHTTPS URL to receive webhook notifications
secretNoSecret for HMAC signing or Bearer token auth
subscriptionsYesArray of event types to subscribe to
enabledYesWhether the subscription is active
auth_typeNoAuthentication method: none, hmac-sha256, or bearer (default: none)

Viewing Delivery History

Track your webhook deliveries to debug issues and monitor delivery success:

curl -X GET "https://api.thrive.com/public/v1/webhook-subscriptions/events?limit=50&status=failed" \
  -H "x-thrive-client-id: your-client-id" \
  -H "x-thrive-client-secret: your-client-secret" \
  -H "x-thrive-merchant-uid: your-merchant-uid"

Query Parameters:

ParameterDescription
limitMaximum events to return (1-100, default: 50)
start_dateFilter events after this date (ISO 8601)
end_dateFilter events before this date (ISO 8601)
event_typeFilter by specific event type
statusFilter by success or failed

The response includes the full payload that was sent, response time, HTTP status, retry count, and any error messages from your endpoint.


Need Help?

Contact your Thrive representative to:

  • Get assistance with webhook configuration
  • Troubleshoot delivery issues
  • Request test webhooks
  • Discuss custom integration requirements

We're here to help you integrate successfully.