Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.zestequity.com/llms.txt

Use this file to discover all available pages before exploring further.

Zest delivers lifecycle events to a single partner-registered HTTPS endpoint. Every event uses the same envelope so a single handler can dispatch on eventType.

Universal envelope

{
  "eventId": "wde_2c5d6b3e9f1a4d7c",
  "eventType": "spv_request.created",
  "occurredAt": "2026-05-07T12:00:00.123Z",
  "data": { /* per-event payload, see Events */ }
}
FieldTypeDescription
eventIdstringGlobally unique delivery id. Use it for dedup.
eventTypestringOne of the nine event types listed below.
occurredAtstring (ISO 8601 UTC)Server time when the event was emitted. Use this for ordering, not receipt order.
dataobjectEvent-specific payload. See per-event reference.

Event types

Event typeTrigger
spv_request.createdA partner POSTed a new SPV request.
spv_request.completedZest admin approved an SPV request and the SPV materialised.
spv_request.rejectedZest admin rejected an SPV request.
spv_request.cancelledA partner cancelled a pending-review SPV request.
investor.createdA partner created a new investor record.
subscription.createdA partner created a subscription on an SPV.
signed_subscription_form.uploadedA partner uploaded a signed subscription form.
funding_receipt.uploadedA partner uploaded a wire-transfer receipt.
subscription.completedZest admin transitioned a partner-sourced bid to Completed.

Delivery semantics

  • At-least-once. Zest may deliver the same event more than once. Always dedup on eventId.
  • No ordering guarantees. Two events for the same subject can arrive out of order. Use the occurredAt timestamp as the source of truth for sequencing.
  • Outbox pattern. Events are persisted in Zest’s database before delivery, so a transient delivery failure never loses an event.
  • Retries. See Retries & dead-letter.

Ordering disclaimer

If you process state transitions that depend on order — for example, subscription.completed MUST follow funding_receipt.uploaded — derive ordering from occurredAt, not arrival order. A naive consumer that overwrites state on every receipt may regress when a delayed earlier event arrives second.

Dedup recipe

seen = redis.set(f"webhook:dedup:{event['eventId']}", "1", ex=86400, nx=True)
if not seen:
    return 200  # already processed; ack and skip
process(event)
A 24-hour dedup window safely covers the full retry tail (~14h45m) plus margin.

What gets delivered

  • HTTP POST to your registered URL.
  • Content-Type: application/json.
  • Body is the envelope above (UTF-8 JSON).
  • Headers:
    • Zest-Signature: t=<unix>,v1=<hex> — see Verification.
    • Zest-Event-Id: <eventId> — convenience copy.
    • Zest-Event-Type: <eventType> — convenience copy.
    • User-Agent: ZestWebhooks/1.0.

Acknowledging receipt

Return any 2xx response within 10 seconds. Zest treats 4xx and 5xx as failures and triggers the retry schedule. We recommend returning 200 quickly and processing the event asynchronously on your side — long-running processing inside the request hot path will exhaust the 10s budget and cause spurious retries.