Skip to main content

SDKs and examples

No official SDKs are published today - the API follows OpenAPI 3.1 so any OpenAPI code generator can produce a typed client for your language. The in-app test console at /manage/developer/console can generate curl, JavaScript fetch, and Python requests snippets from any curated or custom request.

Authentication header (all languages)

Add X-API-Key to every request:

# curl
curl -H "X-API-Key: sk_live_your_key_here" \
https://valara.cloud/api/v1/listings
// JavaScript (fetch)
const response = await fetch("https://valara.cloud/api/v1/listings", {
headers: {
"X-API-Key": process.env.VALARA_API_KEY,
},
});
# Python (requests)
import os
import requests

resp = requests.get(
"https://valara.cloud/api/v1/listings",
headers={"X-API-Key": os.environ["VALARA_API_KEY"]},
)

Create a listing (with idempotency key)

curl -X POST "https://valara.cloud/api/v1/listings" \
-H "X-API-Key: sk_live_your_key_here" \ <!-- pragma: allowlist secret -->
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"address": "123 Main St, Portland, OR 97201",
"owner_user_id": "user_01HXAGENCYOWNER",
"agent_one_id": "user_01HXAGENTONE"
}'
import os
import uuid
import requests

VALARA_API_KEY = os.environ["VALARA_API_KEY"]

def create_listing(address: str, owner_user_id: str, agent_one_id: str) -> dict:
"""
Create a listing via the Valara API.
Uses a UUID v4 idempotency key so safe to retry on network failure.
"""
resp = requests.post(
"https://valara.cloud/api/v1/listings",
headers={
"X-API-Key": VALARA_API_KEY,
"X-Idempotency-Key": str(uuid.uuid4()),
},
json={
"address": address,
"owner_user_id": owner_user_id,
"agent_one_id": agent_one_id,
},
timeout=30,
)
resp.raise_for_status()
return resp.json()

Paginate through all listings

import os
import requests

VALARA_API_KEY = os.environ["VALARA_API_KEY"]

def list_all_listings(status: str = None) -> list[dict]:
"""
Fetch all listings using cursor pagination.
Returns a flat list of all listing objects.
"""
results = []
cursor = None
while True:
params = {"limit": 200}
if status:
params["status"] = status
if cursor:
params["cursor"] = cursor

resp = requests.get(
"https://valara.cloud/api/v1/listings",
headers={"X-API-Key": VALARA_API_KEY},
params=params,
timeout=30,
)
resp.raise_for_status()
data = resp.json()
results.extend(data["items"])

cursor = data.get("next_cursor")
if not cursor:
break # End of collection

return results

Webhook handler with signature verification

Node.js (Express)

import express from "express";
import crypto from "crypto";

const app = express();
// IMPORTANT: Use express.raw() here, not express.json()
// The signature is computed over the raw bytes - JSON parsing changes whitespace
app.use("/webhooks/valara", express.raw({ type: "application/json" }));

const SIGNING_SECRET = process.env.WEBHOOK_SIGNING_SECRET;
// In production use Redis or a database for idempotency tracking
const seenEventIds = new Set();

app.post("/webhooks/valara", (req, res) => {
const signature = req.headers["x-webhook-signature"];
const timestamp = req.headers["x-webhook-timestamp"];
const eventId = req.headers["x-event-id"];
const rawBody = req.body.toString();

// Step 1: Reject stale deliveries (5-minute replay window)
const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - parseInt(timestamp, 10));
if (ageSeconds > 300) {
return res.status(401).send("Timestamp outside replay window");
}

// Step 2: Verify signature with constant-time comparison
const expected = crypto
.createHmac("sha256", SIGNING_SECRET)
.update(`${timestamp}.${rawBody}`)
.digest("hex");

const sigBuf = Buffer.from(signature, "hex");
const expBuf = Buffer.from(expected, "hex");
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {
return res.status(401).send("Invalid signature");
}

// Step 3: Deduplicate (handles retries and replays)
if (seenEventIds.has(eventId)) {
return res.status(200).send("Already processed");
}
seenEventIds.add(eventId);

// Step 4: Process the event
const payload = JSON.parse(rawBody);
console.log(`Received ${payload.event_type} (sandbox=${payload.is_sandbox})`);

// Branch on is_sandbox to avoid duplicate side effects in staging
if (!payload.is_sandbox) {
// Production processing here
}

return res.status(200).send("OK");
});

app.listen(3000);

Python (FastAPI)

import hashlib
import hmac
import os
import time
from fastapi import FastAPI, Header, HTTPException, Request

app = FastAPI()
SIGNING_SECRET = os.environ["WEBHOOK_SIGNING_SECRET"].encode()
# In production use Redis or a database for idempotency tracking
seen_event_ids: set[str] = set()


@app.post("/webhooks/valara")
async def receive_webhook(
request: Request,
x_webhook_signature: str = Header(...),
x_webhook_timestamp: str = Header(...),
x_event_id: str = Header(...),
) -> dict:
"""
Valara webhook receiver.
Verifies HMAC-SHA256 signature before processing.
Always returns 200 on successful verification (even if business logic fails)
to prevent Valara from retrying an already-verified delivery.
"""
raw_body = await request.body()

# Replay window check (5 minutes)
try:
age = abs(int(time.time()) - int(x_webhook_timestamp))
except ValueError:
raise HTTPException(status_code=401, detail="Invalid timestamp")
if age > 300:
raise HTTPException(status_code=401, detail="Timestamp outside replay window")

# Signature verification
expected = hmac.new(
SIGNING_SECRET,
f"{x_webhook_timestamp}.{raw_body.decode()}".encode(),
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(x_webhook_signature, expected):
raise HTTPException(status_code=401, detail="Invalid signature")

# Idempotency check
if x_event_id in seen_event_ids:
return {"status": "already_processed"}
seen_event_ids.add(x_event_id)

payload = await request.json()
print(f"Received {payload['event_type']} (sandbox={payload.get('is_sandbox', False)})")

return {"status": "ok"}

Postman collection

A pre-filled Postman collection is available in the in-app /manage/developer/docs tab. It includes:

  • All 10 public API endpoints with example request bodies
  • Environment variables for VALARA_API_KEY and VALARA_BASE_URL
  • Pre-request scripts to generate idempotency keys automatically
  • Test scripts to assert common response shape invariants

The collection targets the primary host (valara.cloud) by default. Change VALARA_BASE_URL to dash.jacoballenmedia.com if your integration was built against the legacy host.

OpenAPI code generation

Generate a typed client from the spec using any OpenAPI generator:

# Using openapi-generator-cli (Java)
npx @openapitools/openapi-generator-cli generate \
-i https://valara.cloud/api/openapi.json \
-g typescript-fetch \
-o ./generated/valara-client

# Using oapi-codegen (Go)
oapi-codegen -package valara \
https://valara.cloud/api/openapi.json > valara.gen.go

# Using openapi-python-client
openapi-python-client generate \
--url https://valara.cloud/api/openapi.json