> ## Documentation Index
> Fetch the complete documentation index at: https://docs.terabusinessfinance.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Submission status change events pushed to your endpoint.

Tera sends **submission status change events** to your registered HTTPS endpoint whenever a supplier submission's status changes.

Webhooks are the **primary status channel**. Use them to stay in sync with Tera and trigger downstream actions (e.g. update your system, notify the customer, fetch the full Application state).

> **Status polling** is a fallback for resync or recovery only — not a real-time channel.

## Event envelope

Every webhook request is a `POST` to your endpoint with a JSON body matching this shape:

```json theme={null}
{
  "id": "33333333-3333-4333-8333-333333333333",
  "type": "submission.status_changed",
  "occurred_at": "2026-08-29T14:40:00.000Z",
  "data": {
    "submission_id": "22222222-2222-4222-8222-222222222222",
    "external_reference": "your-ref-123",
    "status": "returned",
    "billing_model": "pre_underwriting_cleared",
    "...": "full SupplierVisibleStatus object"
  }
}
```

| Field | Type | Description |
| - | - | - |
| `id` | UUID | Unique webhook delivery ID. |
| `type` | String | Always `"submission.status_changed"`. |
| `occurred_at` | ISO 8601 timestamp | When the status changed. |
| `data` | SupplierVisibleStatus | Full submission state — same object as `GET` responses. Always carries `submission_id` and `external_reference`. |

## Headers

Every webhook request includes:

| Header | Value | Purpose |
| - | - | - |
| `Tera-Webhook-Id` | UUID | Same as `id` field in body. |
| `Tera-Webhook-Timestamp` | Unix timestamp (seconds) | When Tera sent the event. Used for replay-attack detection. |
| `Tera-Webhook-Signature` | `v1=<hex>` | HMAC-SHA256 signature for auth. See [Verify signatures](#verify-signatures). |

## Verify signatures

1. Retrieve your **Webhook Secret** from your supplier settings (format: `whsec_...`).
2. Extract the `Tera-Webhook-Timestamp` and `Tera-Webhook-Signature` headers from the incoming request.
3. Compute the signed payload:
   ```
   <timestamp>.<body_bytes>
   ```
   where `body_bytes` is the raw request body (not parsed JSON).
4. Sign with HMAC-SHA256 using your secret as the key:
   ```
   hex_signature = hmac_sha256("<timestamp>.<body_bytes>", secret_key)
   ```
5. Compare the computed signature with the one in the header (after the `v1=` prefix). If they match, the webhook is authentic.
6. Check that `Tera-Webhook-Timestamp` is within the last 300 seconds to reject replay attacks.

### Example verification (Node.js)

```javascript theme={null}
import crypto from "crypto";

function verifyWebhookSignature(
  body,
  timestamp,
  signature,
  secret
) {
  const payload = `${timestamp}.${body}`;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}
```

## Response handling

**Accept any HTTP 2xx response as successful.** Tera waits up to 10 seconds for your endpoint to respond. If your endpoint:

* Returns any 2xx status: delivery is considered successful
* Returns 3xx, 4xx, or 5xx: delivery fails and will retry
* Times out (>10 seconds): delivery fails and will retry
* Redirects: **redirects are not followed**; treat 3xx as failed

**Retry policy:** Failed deliveries retry with exponential backoff (up to 72 hours). A single successful response ends retries for that event.

## Event lifecycle

1. **Event created** — submission status changes in Pre-Underwriting or shortly after.
2. **Tera sends webhook** — POST to your endpoint with signature.
3. **Your endpoint responds** — any 2xx is treated as success.
4. **Delivery completes** — or enters retry loop on failure.

Each event (`id`) is sent **at most once** per unique state. If the same status is already delivered, Tera will not re-emit.

## See also

* [Webhooks guide](/guides/webhooks) — detailed setup and best practices.
* [Lead Return guide](/guides/lead-return) — handling `status: "returned"` and Return Reason Categories.
* [Status polling](/guides/status-polling) — recovery and resync strategies.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.