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.

The examples below use http://localhost:8080 as the API base URL for local development against the open-source reference implementation. Production base URLs (sandbox + live) are communicated during partner onboarding.
This walkthrough creates an SPV request end to end using the sandbox environment. By the end you will have:
  1. Exchanged a partner JWT for an access token.
  2. Listed available contract templates.
  3. Submitted an SPV creation request with Idempotency-Key.
  4. Verified the resulting spv_request.created webhook.
You need a sandbox client_id, the matching EdDSA private key, and a registered webhook URL + signing secret. Email partners@zestequity.com to provision.

1. Mint a JWT assertion

Build a JWT with the following claims:
ClaimValue
issYour API base URL
subYour client_id
audhttps://sandbox.api.zestequity.com
client_idYour client_id
iatnow (unix seconds)
expnow + 60
Sign it with EdDSA using the private key registered with Zest.

2. Exchange it for an access token

curl -X POST https://sandbox.api.zestequity.com/v1/oauth2/tokens \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  -d "assertion=<JWT>"
Response:
{
  "accessToken": "eyJ...",
  "tokenType": "Bearer",
  "expiresIn": 3600
}

3. Submit an SPV request

curl -X POST https://sandbox.api.zestequity.com/v1/spv-requests \
  -H "Accept: application/json" \
  -H "Authorization: Bearer eyJ..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "templateId": "syndicate_secondary",
    "templateVersion": "1.0.0",
    "attributes": {
      "underlyingCompanySlug": "co_acme",
      "currency": "USD",
      "shareClasses": [
        {
          "name": "Class A",
          "managementFeePercent": "2.0",
          "carryPercent": "20.0"
        }
      ]
    }
  }'
Successful response (201 Created):
{
  "spvRequestSlug": "svr_a1b2c3",
  "status": "pending-review",
  "templateId": "syndicate_secondary",
  "templateVersion": "1.0.0",
  "tenantSlug": "acme",
  "clientId": "client_xyz",
  "attributes": { "...": "..." },
  "createdAt": "2026-05-07T12:00:00Z",
  "updatedAt": "2026-05-07T12:00:00Z"
}

4. Same call in Python

import os
import uuid
import time
import jwt
import requests

CLIENT_ID = os.environ["ZEST_CLIENT_ID"]
PRIVATE_KEY = open(os.environ["ZEST_PRIVATE_KEY_PATH"]).read()
BASE_URL = "https://sandbox.api.zestequity.com"

now = int(time.time())
assertion = jwt.encode(
    {
        "iss": "https://your-api.example.com",
        "sub": CLIENT_ID,
        "aud": BASE_URL,
        "client_id": CLIENT_ID,
        "iat": now,
        "exp": now + 60,
    },
    PRIVATE_KEY,
    algorithm="EdDSA",
)

token = requests.post(
    f"{BASE_URL}/v1/oauth2/tokens",
    data={
        "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
        "assertion": assertion,
    },
).json()["accessToken"]

resp = requests.post(
    f"{BASE_URL}/v1/spv-requests",
    headers={
        "Authorization": f"Bearer {token}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "templateId": "syndicate_secondary",
        "templateVersion": "1.0.0",
        "attributes": {
            "underlyingCompanySlug": "co_acme",
            "currency": "USD",
            "shareClasses": [
                {"name": "Class A", "managementFeePercent": "2.0", "carryPercent": "20.0"}
            ],
        },
    },
)
print(resp.status_code, resp.json())

5. Verify the webhook

You should receive a POST to your webhook URL with body:
{
  "eventId": "wde_...",
  "eventType": "spv_request.created",
  "occurredAt": "2026-05-07T12:00:00Z",
  "data": {
    "spvRequestSlug": "svr_a1b2c3",
    "templateId": "syndicate_secondary",
    "templateVersion": "1.0.0",
    "tenantSlug": "acme",
    "attributes": { "...": "..." },
    "status": "pending-review"
  }
}
Verify the Zest-Signature header before processing — see Verification.

Next steps